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 */
74 _SK_DISK_TYPE_TEST_MAX = SK_DISK_TYPE_SUNPLUS /* only auto test until here */
77 #if __BYTE_ORDER == __LITTLE_ENDIAN
78 #define MAKE_TAG(a,b,c,d) \
79 (((uint32_t) d << 24) | \
80 ((uint32_t) c << 16) | \
81 ((uint32_t) b << 8) | \
84 #define MAKE_TAG(a,b,c,d) \
85 (((uint32_t) a << 24) | \
86 ((uint32_t) b << 16) | \
87 ((uint32_t) c << 8) | \
91 typedef enum SkBlobTag {
92 SK_BLOB_TAG_IDENTIFY = MAKE_TAG('I', 'D', 'F', 'Y'),
93 SK_BLOB_TAG_SMART_STATUS = MAKE_TAG('S', 'M', 'S', 'T'),
94 SK_BLOB_TAG_SMART_DATA = MAKE_TAG('S', 'M', 'D', 'T'),
95 SK_BLOB_TAG_SMART_THRESHOLDS = MAKE_TAG('S', 'M', 'T', 'H')
105 uint8_t identify[512];
106 uint8_t smart_data[512];
107 uint8_t smart_thresholds[512];
109 SkBool identify_valid:1;
110 SkBool smart_data_valid:1;
111 SkBool smart_thresholds_valid:1;
113 SkBool blob_smart_status:1;
114 SkBool blob_smart_status_valid:1;
116 SkIdentifyParsedData identify_parsed_data;
117 SkSmartParsedData smart_parsed_data;
123 typedef enum SkAtaCommand {
124 SK_ATA_COMMAND_IDENTIFY_DEVICE = 0xEC,
125 SK_ATA_COMMAND_IDENTIFY_PACKET_DEVICE = 0xA1,
126 SK_ATA_COMMAND_SMART = 0xB0,
127 SK_ATA_COMMAND_CHECK_POWER_MODE = 0xE5
130 /* ATA SMART subcommands (ATA8 7.52.1) */
131 typedef enum SkSmartCommand {
132 SK_SMART_COMMAND_READ_DATA = 0xD0,
133 SK_SMART_COMMAND_READ_THRESHOLDS = 0xD1,
134 SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE = 0xD4,
135 SK_SMART_COMMAND_ENABLE_OPERATIONS = 0xD8,
136 SK_SMART_COMMAND_DISABLE_OPERATIONS = 0xD9,
137 SK_SMART_COMMAND_RETURN_STATUS = 0xDA
140 static const char *disk_type_to_string(SkDiskType type) {
142 /* %STRINGPOOLSTART% */
143 static const char* const map[_SK_DISK_TYPE_MAX] = {
144 [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = "16 Byte SCSI ATA SAT Passthru",
145 [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = "12 Byte SCSI ATA SAT Passthru",
146 [SK_DISK_TYPE_ATA] = "Native Linux ATA",
147 [SK_DISK_TYPE_SUNPLUS] = "Sunplus SCSI ATA Passthru",
148 [SK_DISK_TYPE_BLOB] = "Blob",
149 [SK_DISK_TYPE_UNKNOWN] = "Unknown"
151 /* %STRINGPOOLSTOP% */
153 if (type >= _SK_DISK_TYPE_MAX)
156 return _P(map[type]);
159 static SkBool disk_smart_is_available(SkDisk *d) {
160 return d->identify_valid && !!(d->identify[164] & 1);
163 static SkBool disk_smart_is_enabled(SkDisk *d) {
164 return d->identify_valid && !!(d->identify[170] & 1);
167 static SkBool disk_smart_is_conveyance_test_available(SkDisk *d) {
168 assert(d->smart_data_valid);
170 return !!(d->smart_data[367] & 32);
172 static SkBool disk_smart_is_short_and_extended_test_available(SkDisk *d) {
173 assert(d->smart_data_valid);
175 return !!(d->smart_data[367] & 16);
178 static SkBool disk_smart_is_start_test_available(SkDisk *d) {
179 assert(d->smart_data_valid);
181 return !!(d->smart_data[367] & 1);
184 static SkBool disk_smart_is_abort_test_available(SkDisk *d) {
185 assert(d->smart_data_valid);
187 return !!(d->smart_data[367] & 41);
190 static int disk_ata_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
191 uint8_t *bytes = cmd_data;
194 assert(d->type == SK_DISK_TYPE_ATA);
198 case SK_DIRECTION_OUT:
200 /* We could use HDIO_DRIVE_TASKFILE here, but
201 * that's a deprecated ioctl(), hence we don't
202 * do it. And we don't need writing anyway. */
207 case SK_DIRECTION_IN: {
210 /* We have HDIO_DRIVE_CMD which can only read, but not write,
211 * and cannot do LBA. We use it for all read commands. */
213 ioctl_data = alloca(4 + *len);
214 memset(ioctl_data, 0, 4 + *len);
216 ioctl_data[0] = (uint8_t) command; /* COMMAND */
217 ioctl_data[1] = ioctl_data[0] == WIN_SMART ? bytes[9] : bytes[3]; /* SECTOR/NSECTOR */
218 ioctl_data[2] = bytes[1]; /* FEATURE */
219 ioctl_data[3] = bytes[3]; /* NSECTOR */
221 if ((ret = ioctl(d->fd, HDIO_DRIVE_CMD, ioctl_data)) < 0)
224 memset(bytes, 0, 12);
225 bytes[11] = ioctl_data[0];
226 bytes[1] = ioctl_data[1];
227 bytes[3] = ioctl_data[2];
229 memcpy(data, ioctl_data+4, *len);
234 case SK_DIRECTION_NONE: {
235 uint8_t ioctl_data[7];
237 /* We have HDIO_DRIVE_TASK which can neither read nor
238 * write, but can do LBA. We use it for all commands that
239 * do neither read nor write */
241 memset(ioctl_data, 0, sizeof(ioctl_data));
243 ioctl_data[0] = (uint8_t) command; /* COMMAND */
244 ioctl_data[1] = bytes[1]; /* FEATURE */
245 ioctl_data[2] = bytes[3]; /* NSECTOR */
247 ioctl_data[3] = bytes[9]; /* LBA LOW */
248 ioctl_data[4] = bytes[8]; /* LBA MID */
249 ioctl_data[5] = bytes[7]; /* LBA HIGH */
250 ioctl_data[6] = bytes[10]; /* SELECT */
252 if ((ret = ioctl(d->fd, HDIO_DRIVE_TASK, ioctl_data)))
255 memset(bytes, 0, 12);
256 bytes[11] = ioctl_data[0];
257 bytes[1] = ioctl_data[1];
258 bytes[3] = ioctl_data[2];
260 bytes[9] = ioctl_data[3];
261 bytes[8] = ioctl_data[4];
262 bytes[7] = ioctl_data[5];
264 bytes[10] = ioctl_data[6];
275 /* Sends a SCSI command block */
276 static int sg_io(int fd, int direction,
277 const void *cdb, size_t cdb_len,
278 void *data, size_t data_len,
279 void *sense, size_t sense_len) {
281 struct sg_io_hdr io_hdr;
283 memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
285 io_hdr.interface_id = 'S';
286 io_hdr.cmdp = (unsigned char*) cdb;
287 io_hdr.cmd_len = cdb_len;
288 io_hdr.dxferp = data;
289 io_hdr.dxfer_len = data_len;
291 io_hdr.mx_sb_len = sense_len;
292 io_hdr.dxfer_direction = direction;
293 io_hdr.timeout = SK_TIMEOUT;
295 return ioctl(fd, SG_IO, &io_hdr);
298 static int disk_passthrough_16_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
299 uint8_t *bytes = cmd_data;
302 uint8_t *desc = sense+8;
305 static const int direction_map[] = {
306 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
307 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
308 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
311 assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_16);
313 /* ATA Pass-Through 16 byte command, as described in "T10 04-262r8
314 * ATA Command Pass-Through":
315 * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */
317 memset(cdb, 0, sizeof(cdb));
319 cdb[0] = 0x85; /* OPERATION CODE: 16 byte pass through */
321 if (direction == SK_DIRECTION_NONE) {
322 cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */
323 cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */
325 } else if (direction == SK_DIRECTION_IN) {
326 cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
327 cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
329 } else if (direction == SK_DIRECTION_OUT) {
330 cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */
331 cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */
334 cdb[3] = bytes[0]; /* FEATURES */
337 cdb[5] = bytes[2]; /* SECTORS */
340 cdb[8] = bytes[9]; /* LBA LOW */
341 cdb[10] = bytes[8]; /* LBA MID */
342 cdb[12] = bytes[7]; /* LBA HIGH */
344 cdb[13] = bytes[10] & 0x4F; /* SELECT */
345 cdb[14] = (uint8_t) command;
347 memset(sense, 0, sizeof(sense));
349 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, (size_t) cdb[6] * 512, sense, sizeof(sense))) < 0)
352 if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) {
357 memset(bytes, 0, 12);
365 bytes[10] = desc[12];
366 bytes[11] = desc[13];
371 static int disk_passthrough_12_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
372 uint8_t *bytes = cmd_data;
375 uint8_t *desc = sense+8;
378 static const int direction_map[] = {
379 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
380 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
381 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
384 assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12);
386 /* ATA Pass-Through 12 byte command, as described in "T10 04-262r8
387 * ATA Command Pass-Through":
388 * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */
390 memset(cdb, 0, sizeof(cdb));
392 cdb[0] = 0xa1; /* OPERATION CODE: 12 byte pass through */
394 if (direction == SK_DIRECTION_NONE) {
395 cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */
396 cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */
398 } else if (direction == SK_DIRECTION_IN) {
399 cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
400 cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
402 } else if (direction == SK_DIRECTION_OUT) {
403 cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */
404 cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */
407 cdb[3] = bytes[1]; /* FEATURES */
408 cdb[4] = bytes[3]; /* SECTORS */
410 cdb[5] = bytes[9]; /* LBA LOW */
411 cdb[6] = bytes[8]; /* LBA MID */
412 cdb[7] = bytes[7]; /* LBA HIGH */
414 cdb[8] = bytes[10] & 0x4F; /* SELECT */
415 cdb[9] = (uint8_t) command;
417 memset(sense, 0, sizeof(sense));
419 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, (size_t) cdb[4] * 512, sense, sizeof(sense))) < 0)
422 if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) {
427 memset(bytes, 0, 12);
429 bytes[1] = desc[3]; /* FEATURES */
430 bytes[2] = desc[4]; /* STATUS */
431 bytes[3] = desc[5]; /* SECTORS */
432 bytes[9] = desc[7]; /* LBA LOW */
433 bytes[8] = desc[9]; /* LBA MID */
434 bytes[7] = desc[11]; /* LBA HIGH */
435 bytes[10] = desc[12]; /* SELECT */
436 bytes[11] = desc[13]; /* ERROR */
441 static int disk_sunplus_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
442 uint8_t *bytes = cmd_data;
444 uint8_t sense[32], buf[8];
446 static const int direction_map[] = {
447 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
448 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
449 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
452 assert(d->type == SK_DISK_TYPE_SUNPLUS);
454 /* SunplusIT specific SCSI ATA pass-thru */
456 memset(cdb, 0, sizeof(cdb));
458 cdb[0] = 0xF8; /* OPERATION CODE: Sunplus specific */
459 cdb[1] = 0x00; /* Subcommand: Pass-thru */
462 if (direction == SK_DIRECTION_NONE)
463 cdb[3] = 0x00; /* protocol */
464 else if (direction == SK_DIRECTION_IN)
465 cdb[3] = 0x10; /* protocol */
466 else if (direction == SK_DIRECTION_OUT)
467 cdb[3] = 0x11; /* protocol */
469 cdb[4] = bytes[3]; /* size? */
470 cdb[5] = bytes[1]; /* FEATURES */
471 cdb[6] = bytes[3]; /* SECTORS */
472 cdb[7] = bytes[9]; /* LBA LOW */
473 cdb[8] = bytes[8]; /* LBA MID */
474 cdb[9] = bytes[7]; /* LBA HIGH */
475 cdb[10] = bytes[10] | 0xA0; /* SELECT */
476 cdb[11] = (uint8_t) command;
478 memset(sense, 0, sizeof(sense));
481 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, (size_t) cdb[6] * 512, sense, sizeof(sense))) < 0)
484 memset(cdb, 0, sizeof(cdb));
490 /* Ask for response */
491 if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), buf, sizeof(buf), sense, sizeof(sense))) < 0)
494 memset(bytes, 0, 12);
496 bytes[2] = buf[1]; /* ERROR */
497 bytes[3] = buf[2]; /* SECTORS */
498 bytes[9] = buf[3]; /* LBA LOW */
499 bytes[8] = buf[4]; /* LBA MID */
500 bytes[7] = buf[5]; /* LBA HIGH */
501 bytes[10] = buf[6]; /* SELECT */
502 bytes[11] = buf[7]; /* STATUS */
507 static int disk_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
509 static int (* const disk_command_table[_SK_DISK_TYPE_MAX]) (SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) = {
510 [SK_DISK_TYPE_ATA] = disk_ata_command,
511 [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = disk_passthrough_12_command,
512 [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = disk_passthrough_16_command,
513 [SK_DISK_TYPE_SUNPLUS] = disk_sunplus_command
517 assert(d->type <= _SK_DISK_TYPE_MAX);
518 assert(direction <= _SK_DIRECTION_MAX);
520 assert(direction == SK_DIRECTION_NONE || (data && len && *len > 0));
521 assert(direction != SK_DIRECTION_NONE || (!data && !len));
523 return disk_command_table[d->type](d, command, direction, cmd_data, data, len);
526 static int disk_identify_device(SkDisk *d) {
531 if (d->type == SK_DISK_TYPE_BLOB)
534 memset(cmd, 0, sizeof(cmd));
538 if ((ret = disk_command(d, SK_ATA_COMMAND_IDENTIFY_DEVICE, SK_DIRECTION_IN, cmd, d->identify, &len)) < 0)
546 d->identify_valid = TRUE;
551 int sk_disk_check_sleep_mode(SkDisk *d, SkBool *awake) {
556 if (!d->identify_valid) {
561 if (d->type == SK_DISK_TYPE_BLOB) {
566 memset(cmd, 0, sizeof(cmd));
568 if ((ret = disk_command(d, SK_ATA_COMMAND_CHECK_POWER_MODE, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
571 if (cmd[0] != 0 || (ntohs(cmd[5]) & 1) != 0) {
576 status = ntohs(cmd[1]) & 0xFF;
577 *awake = status == 0xFF || status == 0x80; /* idle and active/idle is considered awake */
582 static int disk_smart_enable(SkDisk *d, SkBool b) {
585 if (!disk_smart_is_available(d)) {
590 if (d->type == SK_DISK_TYPE_BLOB) {
595 memset(cmd, 0, sizeof(cmd));
597 cmd[0] = htons(b ? SK_SMART_COMMAND_ENABLE_OPERATIONS : SK_SMART_COMMAND_DISABLE_OPERATIONS);
598 cmd[2] = htons(0x0000U);
599 cmd[3] = htons(0x00C2U);
600 cmd[4] = htons(0x4F00U);
602 return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0);
605 int sk_disk_smart_read_data(SkDisk *d) {
610 if (!disk_smart_is_available(d)) {
615 if (d->type == SK_DISK_TYPE_BLOB)
618 memset(cmd, 0, sizeof(cmd));
620 cmd[0] = htons(SK_SMART_COMMAND_READ_DATA);
622 cmd[2] = htons(0x0000U);
623 cmd[3] = htons(0x00C2U);
624 cmd[4] = htons(0x4F00U);
626 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_data, &len)) < 0)
629 d->smart_data_valid = TRUE;
634 static int disk_smart_read_thresholds(SkDisk *d) {
639 if (!disk_smart_is_available(d)) {
644 if (d->type == SK_DISK_TYPE_BLOB)
647 memset(cmd, 0, sizeof(cmd));
649 cmd[0] = htons(SK_SMART_COMMAND_READ_THRESHOLDS);
651 cmd[2] = htons(0x0000U);
652 cmd[3] = htons(0x00C2U);
653 cmd[4] = htons(0x4F00U);
655 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_thresholds, &len)) < 0)
658 d->smart_thresholds_valid = TRUE;
663 int sk_disk_smart_status(SkDisk *d, SkBool *good) {
667 if (!disk_smart_is_available(d)) {
672 if (d->type == SK_DISK_TYPE_BLOB) {
674 if (d->blob_smart_status_valid) {
675 *good = d->blob_smart_status;
683 memset(cmd, 0, sizeof(cmd));
685 cmd[0] = htons(SK_SMART_COMMAND_RETURN_STATUS);
686 cmd[1] = htons(0x0000U);
687 cmd[3] = htons(0x00C2U);
688 cmd[4] = htons(0x4F00U);
690 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
693 /* SAT/USB bridges truncate packets, so we only check for 4F,
694 * not for 2C on those */
695 if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x00C2U)) &&
696 cmd[4] == htons(0x4F00U))
698 else if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x002CU)) &&
699 cmd[4] == htons(0xF400U))
709 int sk_disk_smart_self_test(SkDisk *d, SkSmartSelfTest test) {
713 if (!disk_smart_is_available(d)) {
718 if (d->type == SK_DISK_TYPE_BLOB) {
723 if (!d->smart_data_valid)
724 if ((ret = sk_disk_smart_read_data(d)) < 0)
727 assert(d->smart_data_valid);
729 if (test != SK_SMART_SELF_TEST_SHORT &&
730 test != SK_SMART_SELF_TEST_EXTENDED &&
731 test != SK_SMART_SELF_TEST_CONVEYANCE &&
732 test != SK_SMART_SELF_TEST_ABORT) {
737 if (!disk_smart_is_start_test_available(d)
738 || (test == SK_SMART_SELF_TEST_ABORT && !disk_smart_is_abort_test_available(d))
739 || ((test == SK_SMART_SELF_TEST_SHORT || test == SK_SMART_SELF_TEST_EXTENDED) && !disk_smart_is_short_and_extended_test_available(d))
740 || (test == SK_SMART_SELF_TEST_CONVEYANCE && !disk_smart_is_conveyance_test_available(d))) {
745 if (test == SK_SMART_SELF_TEST_ABORT &&
746 !disk_smart_is_abort_test_available(d)) {
751 memset(cmd, 0, sizeof(cmd));
753 cmd[0] = htons(SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE);
754 cmd[2] = htons(0x0000U);
755 cmd[3] = htons(0x00C2U);
756 cmd[4] = htons(0x4F00U | (uint16_t) test);
758 return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, NULL);
761 static void swap_strings(char *s, size_t len) {
762 assert((len & 1) == 0);
764 for (; len > 0; s += 2, len -= 2) {
772 static void clean_strings(char *s) {
776 if (*e < ' ' || *e >= 127)
780 static void drop_spaces(char *s) {
782 SkBool prev_space = FALSE;
805 static void read_string(char *d, uint8_t *s, size_t len) {
808 swap_strings(d, len);
813 int sk_disk_identify_parse(SkDisk *d, const SkIdentifyParsedData **ipd) {
817 if (!d->identify_valid) {
822 read_string(d->identify_parsed_data.serial, d->identify+20, 20);
823 read_string(d->identify_parsed_data.firmware, d->identify+46, 8);
824 read_string(d->identify_parsed_data.model, d->identify+54, 40);
826 *ipd = &d->identify_parsed_data;
831 int sk_disk_smart_is_available(SkDisk *d, SkBool *b) {
835 if (!d->identify_valid) {
840 *b = disk_smart_is_available(d);
844 int sk_disk_identify_is_available(SkDisk *d, SkBool *b) {
848 *b = d->identify_valid;
852 const char *sk_smart_offline_data_collection_status_to_string(SkSmartOfflineDataCollectionStatus status) {
854 /* %STRINGPOOLSTART% */
855 static const char* const map[] = {
856 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER] = "Off-line data collection activity was never started.",
857 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS] = "Off-line data collection activity was completed without error.",
858 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS] = "Off-line activity in progress.",
859 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED] = "Off-line data collection activity was suspended by an interrupting command from host.",
860 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED] = "Off-line data collection activity was aborted by an interrupting command from host.",
861 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL] = "Off-line data collection activity was aborted by the device with a fatal error.",
862 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN] = "Unknown status"
864 /* %STRINGPOOLSTOP% */
866 if (status >= _SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_MAX)
869 return _P(map[status]);
872 const char *sk_smart_self_test_execution_status_to_string(SkSmartSelfTestExecutionStatus status) {
874 /* %STRINGPOOLSTART% */
875 static const char* const map[] = {
876 [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.",
877 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED] = "The self-test routine was aborted by the host.",
878 [SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED] = "The self-test routine was interrupted by the host with a hardware or software reset.",
879 [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.",
880 [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.",
881 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL] = "The previous self-test completed having the electrical element of the test failed.",
882 [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.",
883 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ] = "The previous self-test completed having the read element of the test failed.",
884 [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.",
885 [SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS] = "Self-test routine in progress"
887 /* %STRINGPOOLSTOP% */
889 if (status >= _SK_SMART_SELF_TEST_EXECUTION_STATUS_MAX)
892 return _P(map[status]);
895 const char* sk_smart_self_test_to_string(SkSmartSelfTest test) {
898 case SK_SMART_SELF_TEST_SHORT:
900 case SK_SMART_SELF_TEST_EXTENDED:
902 case SK_SMART_SELF_TEST_CONVEYANCE:
904 case SK_SMART_SELF_TEST_ABORT:
911 SkBool sk_smart_self_test_available(const SkSmartParsedData *d, SkSmartSelfTest test) {
914 if (!d->start_test_available)
918 case SK_SMART_SELF_TEST_SHORT:
919 case SK_SMART_SELF_TEST_EXTENDED:
920 return d->short_and_extended_test_available;
921 case SK_SMART_SELF_TEST_CONVEYANCE:
922 return d->conveyance_test_available;
923 case SK_SMART_SELF_TEST_ABORT:
924 return d->abort_test_available;
930 unsigned sk_smart_self_test_polling_minutes(const SkSmartParsedData *d, SkSmartSelfTest test) {
933 if (!sk_smart_self_test_available(d, test))
937 case SK_SMART_SELF_TEST_SHORT:
938 return d->short_test_polling_minutes;
939 case SK_SMART_SELF_TEST_EXTENDED:
940 return d->extended_test_polling_minutes;
941 case SK_SMART_SELF_TEST_CONVEYANCE:
942 return d->conveyance_test_polling_minutes;
948 static void make_pretty(SkSmartAttributeParsedData *a) {
949 uint64_t fourtyeight;
954 if (a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_UNKNOWN)
958 ((uint64_t) a->raw[0]) |
959 (((uint64_t) a->raw[1]) << 8) |
960 (((uint64_t) a->raw[2]) << 16) |
961 (((uint64_t) a->raw[3]) << 24) |
962 (((uint64_t) a->raw[4]) << 32) |
963 (((uint64_t) a->raw[5]) << 40);
965 if (!strcmp(a->name, "spin-up-time"))
966 a->pretty_value = fourtyeight & 0xFFFF;
967 else if (!strcmp(a->name, "airflow-temperature-celsius") ||
968 !strcmp(a->name, "temperature-celsius") ||
969 !strcmp(a->name, "temperature-celsius-2"))
970 a->pretty_value = (fourtyeight & 0xFFFF)*1000 + 273150;
971 else if (!strcmp(a->name, "temperature-centi-celsius"))
972 a->pretty_value = (fourtyeight & 0xFFFF)*100 + 273150;
973 else if (!strcmp(a->name, "power-on-minutes"))
974 a->pretty_value = (((uint64_t) a->raw[0]) | (uint64_t) a->raw[1]) * 60 * 1000;
975 else if (!strcmp(a->name, "power-on-seconds"))
976 a->pretty_value = fourtyeight * 1000;
977 else if (!strcmp(a->name, "power-on-half-minutes"))
978 a->pretty_value = fourtyeight * 30 * 1000;
979 else if (!strcmp(a->name, "power-on-hours") ||
980 !strcmp(a->name, "loaded-hours") ||
981 !strcmp(a->name, "head-flying-hours"))
982 a->pretty_value = fourtyeight * 60 * 60 * 1000;
984 a->pretty_value = fourtyeight;
987 typedef struct SkSmartAttributeInfo {
989 SkSmartAttributeUnit unit;
990 } SkSmartAttributeInfo;
992 /* This data is stolen from smartmontools */
994 /* %STRINGPOOLSTART% */
995 static const SkSmartAttributeInfo const attribute_info[256] = {
996 [1] = { "raw-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
997 [2] = { "throughput-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
998 [3] = { "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
999 [4] = { "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1000 [5] = { "reallocated-sector-count", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1001 [6] = { "read-channel-margin", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1002 [7] = { "seek-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1003 [8] = { "seek-time-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1004 [9] = { "power-on-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1005 [10] = { "spin-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1006 [11] = { "calibration-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1007 [12] = { "power-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1008 [13] = { "read-soft-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1009 [187] = { "reported-uncorrect", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1010 [189] = { "high-fly-writes", SK_SMART_ATTRIBUTE_UNIT_NONE },
1011 [190] = { "airflow-temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN },
1012 [191] = { "g-sense-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1013 [192] = { "power-off-retract-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1014 [193] = { "load-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1015 [194] = { "temperature-celsius-2", SK_SMART_ATTRIBUTE_UNIT_MKELVIN },
1016 [195] = { "hardware-ecc-recovered", SK_SMART_ATTRIBUTE_UNIT_NONE },
1017 [196] = { "reallocated-event-count", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1018 [197] = { "current-pending-sector", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1019 [198] = { "offline-uncorrectable", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1020 [199] = { "udma-crc-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1021 [200] = { "multi-zone-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1022 [201] = { "soft-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1023 [202] = { "ta-increase-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1024 [203] = { "run-out-cancel", SK_SMART_ATTRIBUTE_UNIT_NONE },
1025 [204] = { "shock-count-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE },
1026 [205] = { "shock-rate-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE },
1027 [206] = { "flying-height", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1028 [207] = { "spin-high-current", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1029 [208] = { "spin-buzz", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN},
1030 [209] = { "offline-seek-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1031 [220] = { "disk-shift", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1032 [221] = { "g-sense-error-rate-2", SK_SMART_ATTRIBUTE_UNIT_NONE },
1033 [222] = { "loaded-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1034 [223] = { "load-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1035 [224] = { "load-friction", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1036 [225] = { "load-cycle-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE },
1037 [226] = { "load-in-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1038 [227] = { "torq-amp-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1039 [228] = { "power-off-retract-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE },
1040 [230] = { "head-amplitude", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1041 [231] = { "temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN },
1042 [240] = { "head-flying-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1043 [250] = { "read-error-retry-rate", SK_SMART_ATTRIBUTE_UNIT_NONE }
1045 /* %STRINGPOOLSTOP% */
1047 typedef enum SkSmartQuirk {
1048 SK_SMART_QUIRK_9_POWERONMINUTES = 1,
1049 SK_SMART_QUIRK_9_POWERONSECONDS = 2,
1050 SK_SMART_QUIRK_9_POWERONHALFMINUTES = 4,
1051 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT = 8,
1052 SK_SMART_QUIRK_193_LOADUNLOAD = 16,
1053 SK_SMART_QUIRK_194_10XCELSIUS = 32,
1054 SK_SMART_QUIRK_194_UNKNOWN = 64,
1055 SK_SMART_QUIRK_200_WRITEERRORCOUNT = 128,
1056 SK_SMART_QUIRK_201_DETECTEDTACOUNT = 256,
1059 /* %STRINGPOOLSTART% */
1060 static const char *quirk_name[] = {
1063 "9_POWERONHALFMINUTES",
1064 "192_EMERGENCYRETRACTCYCLECT",
1068 "200_WRITEERRORCOUNT",
1069 "201_DETECTEDTACOUNT",
1072 /* %STRINGPOOLSTOP% */
1074 typedef struct SkSmartQuirkDatabase {
1076 const char *firmware;
1078 } SkSmartQuirkDatabase;
1080 static const SkSmartQuirkDatabase quirk_database[] = { {
1083 "^FUJITSU MHR2040AT$",
1085 SK_SMART_QUIRK_9_POWERONSECONDS|
1086 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1087 SK_SMART_QUIRK_200_WRITEERRORCOUNT
1089 "^FUJITSU MHS20[6432]0AT( .)?$",
1091 SK_SMART_QUIRK_9_POWERONSECONDS|
1092 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1093 SK_SMART_QUIRK_200_WRITEERRORCOUNT|
1094 SK_SMART_QUIRK_201_DETECTEDTACOUNT
1098 "FUJITSU MHG2...ATU?.*|"
1099 "FUJITSU MHH2...ATU?.*|"
1100 "FUJITSU MHJ2...ATU?.*|"
1101 "FUJITSU MHK2...ATU?.*|"
1102 "FUJITSU MHL2300AT|"
1103 "FUJITSU MHM2(20|15|10|06)0AT|"
1104 "FUJITSU MHN2...AT|"
1105 "FUJITSU MHR2020AT|"
1106 "FUJITSU MHT2...(AH|AS|AT|BH)U?.*|"
1107 "FUJITSU MHU2...ATU?.*|"
1108 "FUJITSU MHV2...(AH|AS|AT|BH|BS|BT).*|"
1109 "FUJITSU MP[A-G]3...A[HTEV]U?.*"
1112 SK_SMART_QUIRK_9_POWERONSECONDS
1118 "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]"
1121 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1128 SK_SMART_QUIRK_9_POWERONHALFMINUTES|
1129 SK_SMART_QUIRK_194_10XCELSIUS
1131 "^SAMSUNG SP40A2H$",
1133 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1135 "^SAMSUNG SP80A4H$",
1137 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1139 "^SAMSUNG SP8004H$",
1141 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1146 "Maxtor 2B0(0[468]|1[05]|20)H1|"
1147 "Maxtor 4G(120J6|160J[68])|"
1148 "Maxtor 4D0(20H1|40H2|60H3|80H4)"
1151 SK_SMART_QUIRK_9_POWERONMINUTES|
1152 SK_SMART_QUIRK_194_UNKNOWN
1155 "Maxtor 2F0[234]0[JL]0|"
1156 "Maxtor 8(1280A2|2160A4|2560A4|3840A6|4000A6|5120A8)|"
1157 "Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)|"
1158 "Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)|"
1159 "Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)|"
1160 "Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)|"
1161 "Maxtor 9(2732U8|2390U7|204[09]U6|1707U5|1366U4|1024U3|0845U3|0683U2)|"
1162 "Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)|"
1163 "Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D[34]|90432D2)|"
1164 "Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)|"
1165 "Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)|"
1166 "Maxtor (33073U4|32049U3|31536U2|30768U1|33073H4|32305H3|31536H2|30768H1)|"
1167 "Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)|"
1168 "Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)|"
1169 "Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)|"
1170 "Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?|"
1171 "Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)|"
1172 "Maxtor 9(1023U2|1536U2|2049U3|2305U3|3073U4|4610U6|6147U8)|"
1173 "Maxtor 9(1023H2|1536H2|2049H3|2305H3|3073H4|4098H6|4610H6|6147H8)|"
1174 "Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)|"
1175 "Maxtor (98196H8|96147H6)|"
1176 "Maxtor 4W(100H6|080H6|060H4|040H3|030H2)|"
1177 "Maxtor 6(E0[234]|K04)0L0|"
1178 "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|"
1179 "Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)|"
1180 "Maxtor 7Y250[PM]0|"
1181 "Maxtor [45]A(25|30|32)0[JN]0|"
1182 "Maxtor 7L(25|30)0[SR]0"
1185 SK_SMART_QUIRK_9_POWERONMINUTES
1191 "HITACHI_DK14FA-20B|"
1192 "HITACHI_DK23..-..B?|"
1193 "HITACHI_DK23FA-20J|HTA422020F9AT[JN]0|"
1194 "HE[JN]4230[23]0F9AT00|"
1195 "HTC4260[23]0G5CE00|HTC4260[56]0G8CE00"
1198 SK_SMART_QUIRK_9_POWERONMINUTES|
1199 SK_SMART_QUIRK_193_LOADUNLOAD
1208 static int match(const char*regex, const char *s, SkBool *result) {
1214 if (regcomp(&re, regex, REG_EXTENDED|REG_NOSUB) != 0) {
1219 if ((k = regexec(&re, s, 0, NULL, 0)) != 0) {
1221 if (k != REG_NOMATCH) {
1235 static int lookup_quirks(const char *model, const char *firmware, SkSmartQuirk *quirk) {
1237 const SkSmartQuirkDatabase *db;
1241 for (db = quirk_database; db->model || db->firmware; db++) {
1244 SkBool matching = FALSE;
1246 if ((k = match(db->model, model, &matching)) < 0)
1254 SkBool matching = FALSE;
1256 if ((k = match(db->firmware, firmware, &matching)) < 0)
1270 static const SkSmartAttributeInfo *lookup_attribute(SkDisk *d, uint8_t id) {
1271 const SkIdentifyParsedData *ipd;
1272 SkSmartQuirk quirk = 0;
1274 /* These are the complex ones */
1275 if (sk_disk_identify_parse(d, &ipd) < 0)
1278 if (lookup_quirks(ipd->model, ipd->firmware, &quirk) < 0)
1285 /* %STRINGPOOLSTART% */
1286 if (quirk & SK_SMART_QUIRK_9_POWERONMINUTES) {
1287 static const SkSmartAttributeInfo a = {
1288 "power-on-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1292 } else if (quirk & SK_SMART_QUIRK_9_POWERONSECONDS) {
1293 static const SkSmartAttributeInfo a = {
1294 "power-on-seconds", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1298 } else if (quirk & SK_SMART_QUIRK_9_POWERONHALFMINUTES) {
1299 static const SkSmartAttributeInfo a = {
1300 "power-on-half-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1304 /* %STRINGPOOLSTOP% */
1309 /* %STRINGPOOLSTART% */
1310 if (quirk & SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) {
1311 static const SkSmartAttributeInfo a = {
1312 "emergency-retract-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1316 /* %STRINGPOOLSTOP% */
1321 /* %STRINGPOOLSTART% */
1322 if (quirk & SK_SMART_QUIRK_194_10XCELSIUS) {
1323 static const SkSmartAttributeInfo a = {
1324 "temperature-centi-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN
1327 } else if (quirk & SK_SMART_QUIRK_194_UNKNOWN)
1329 /* %STRINGPOOLSTOP% */
1334 /* %STRINGPOOLSTART% */
1335 if (quirk & SK_SMART_QUIRK_200_WRITEERRORCOUNT) {
1336 static const SkSmartAttributeInfo a = {
1337 "write-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1341 /* %STRINGPOOLSTOP% */
1346 /* %STRINGPOOLSTART% */
1347 if (quirk & SK_SMART_QUIRK_201_DETECTEDTACOUNT) {
1348 static const SkSmartAttributeInfo a = {
1349 "detected-ta-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1353 /* %STRINGPOOLSTOP% */
1359 /* These are the simple cases */
1360 if (attribute_info[id].name)
1361 return &attribute_info[id];
1366 int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **spd) {
1368 if (!d->smart_data_valid) {
1373 switch (d->smart_data[362]) {
1376 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER;
1381 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS;
1385 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS;
1390 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED;
1395 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED;
1400 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL;
1404 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN;
1408 d->smart_parsed_data.self_test_execution_percent_remaining = 10*(d->smart_data[363] & 0xF);
1409 d->smart_parsed_data.self_test_execution_status = (d->smart_data[363] >> 4) & 0xF;
1411 d->smart_parsed_data.total_offline_data_collection_seconds = (uint16_t) d->smart_data[364] | ((uint16_t) d->smart_data[365] << 8);
1413 d->smart_parsed_data.conveyance_test_available = disk_smart_is_conveyance_test_available(d);
1414 d->smart_parsed_data.short_and_extended_test_available = disk_smart_is_short_and_extended_test_available(d);
1415 d->smart_parsed_data.start_test_available = disk_smart_is_start_test_available(d);
1416 d->smart_parsed_data.abort_test_available = disk_smart_is_abort_test_available(d);
1418 d->smart_parsed_data.short_test_polling_minutes = d->smart_data[372];
1419 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]);
1420 d->smart_parsed_data.conveyance_test_polling_minutes = d->smart_data[374];
1422 *spd = &d->smart_parsed_data;
1427 static void find_threshold(SkDisk *d, SkSmartAttributeParsedData *a) {
1431 if (!d->smart_thresholds_valid) {
1432 a->threshold_valid = FALSE;
1436 for (n = 0, p = d->smart_thresholds+2; n < 30; n++, p+=12)
1441 a->threshold_valid = FALSE;
1442 a->good_valid = FALSE;
1446 a->threshold = p[1];
1447 a->threshold_valid = p[1] != 0xFE;
1449 a->good_valid = FALSE;
1452 /* Always-Fail and Always-Passing thresholds are not relevant
1453 * for our assessment. */
1454 if (p[1] >= 1 && p[1] <= 0xFD) {
1456 if (a->worst_value_valid) {
1457 a->good = a->good && (a->worst_value > a->threshold);
1458 a->good_valid = TRUE;
1461 if (a->current_value_valid) {
1462 a->good = a->good && (a->current_value > a->threshold);
1463 a->good_valid = TRUE;
1468 int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, void* userdata) {
1472 if (!d->smart_data_valid) {
1477 for (n = 0, p = d->smart_data + 2; n < 30; n++, p+=12) {
1478 SkSmartAttributeParsedData a;
1479 const SkSmartAttributeInfo *i;
1485 memset(&a, 0, sizeof(a));
1487 a.current_value = p[3];
1488 a.current_value_valid = p[3] >= 1 && p[3] <= 0xFD;
1489 a.worst_value = p[4];
1490 a.worst_value_valid = p[4] >= 1 && p[4] <= 0xFD;
1492 a.flags = ((uint16_t) p[2] << 8) | p[1];
1493 a.prefailure = !!(p[1] & 1);
1494 a.online = !!(p[1] & 2);
1496 memcpy(a.raw, p+5, 6);
1498 if ((i = lookup_attribute(d, p[0]))) {
1499 a.name = _P(i->name);
1500 a.pretty_unit = i->unit;
1502 if (asprintf(&an, "attribute-%u", a.id) < 0) {
1508 a.pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1513 find_threshold(d, &a);
1515 /* Handle a few fields specially */
1516 if ((!strcmp(a.name, "reallocated-sector-count") ||
1517 !strcmp(a.name, "reallocated-event-count") ||
1518 !strcmp(a.name, "current-pending-sector")) &&
1519 a.pretty_unit == SK_SMART_ATTRIBUTE_UNIT_SECTORS &&
1520 a.pretty_value > 0) {
1522 a.good_valid = TRUE;
1525 cb(d, &a, userdata);
1533 static const char *yes_no(SkBool b) {
1534 return b ? "yes" : "no";
1537 const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit) {
1539 /* %STRINGPOOLSTART% */
1540 const char * const map[] = {
1541 [SK_SMART_ATTRIBUTE_UNIT_UNKNOWN] = NULL,
1542 [SK_SMART_ATTRIBUTE_UNIT_NONE] = "",
1543 [SK_SMART_ATTRIBUTE_UNIT_MSECONDS] = "ms",
1544 [SK_SMART_ATTRIBUTE_UNIT_SECTORS] = "sectors",
1545 [SK_SMART_ATTRIBUTE_UNIT_MKELVIN] = "mK"
1547 /* %STRINGPOOLSTOP% */
1549 if (unit >= _SK_SMART_ATTRIBUTE_UNIT_MAX)
1552 return _P(map[unit]);
1555 struct attr_helper {
1560 static void temperature_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1562 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MKELVIN)
1565 if (!strcmp(a->name, "temperature-centi-celsius") ||
1566 !strcmp(a->name, "temperature-celsius") ||
1567 !strcmp(a->name, "temperature-celsius-2") ||
1568 !strcmp(a->name, "airflow-temperature-celsius")) {
1570 if (!ah->found || a->pretty_value > *ah->value)
1571 *ah->value = a->pretty_value;
1577 int sk_disk_smart_get_temperature(SkDisk *d, uint64_t *kelvin) {
1578 struct attr_helper ah;
1586 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) temperature_cb, &ah) < 0)
1597 static void power_on_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1599 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MSECONDS)
1602 if (!strcmp(a->name, "power-on-minutes") ||
1603 !strcmp(a->name, "power-on-seconds") ||
1604 !strcmp(a->name, "power-on-half-minutes") ||
1605 !strcmp(a->name, "power-on-hours")) {
1607 if (!ah->found || a->pretty_value > *ah->value)
1608 *ah->value = a->pretty_value;
1614 int sk_disk_smart_get_power_on(SkDisk *d, uint64_t *mseconds) {
1615 struct attr_helper ah;
1621 ah.value = mseconds;
1623 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_on_cb, &ah) < 0)
1634 static void reallocated_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1636 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
1639 if (!strcmp(a->name, "reallocated-sector-count") ||
1640 !strcmp(a->name, "reallocated-event-count")) {
1642 if (!ah->found || a->pretty_value > *ah->value)
1643 *ah->value = a->pretty_value;
1649 static void pending_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1651 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
1654 if (!strcmp(a->name, "current-pending-sector")) {
1656 if (!ah->found || a->pretty_value > *ah->value)
1657 *ah->value = a->pretty_value;
1663 int sk_disk_smart_get_bad(SkDisk *d, uint64_t *sectors) {
1664 struct attr_helper ah1, ah2;
1665 uint64_t sectors1, sectors2;
1671 ah1.value = §ors1;
1673 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) reallocated_cb, &ah1) < 0)
1677 ah2.value = §ors2;
1679 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) pending_cb, &ah2) < 0)
1682 if (!ah1.found && !ah2.found) {
1687 if (ah1.found && ah2.found)
1688 *sectors = sectors1 + sectors2;
1690 *sectors = sectors1;
1692 *sectors = sectors2;
1697 const char* sk_smart_overall_to_string(SkSmartOverall overall) {
1699 /* %STRINGPOOLSTART% */
1700 const char * const map[] = {
1701 [SK_SMART_OVERALL_GOOD] = "GOOD",
1702 [SK_SMART_OVERALL_BAD_STATUS] = "BAD_STATUS",
1703 [SK_SMART_OVERALL_BAD_ATTRIBUTE] = "BAD_ATTRIBUTE",
1704 [SK_SMART_OVERALL_BAD_SECTOR] = "BAD_SECTOR"
1706 /* %STRINGPOOLSTOP% */
1708 if (overall >= _SK_SMART_OVERALL_MAX)
1711 return _P(map[overall]);
1714 static void bad_attribute_cb(SkDisk *d, const SkSmartAttributeParsedData *a, SkBool *good) {
1715 if (a->good_valid && !a->good)
1719 int sk_disk_smart_get_overall(SkDisk *d, SkSmartOverall *overall) {
1726 if (sk_disk_smart_status(d, &good) < 0)
1730 *overall = SK_SMART_OVERALL_BAD_STATUS;
1734 if (sk_disk_smart_get_bad(d, §ors) < 0) {
1735 if (errno != ENOENT)
1737 } else if (sectors > 0) {
1738 *overall = SK_SMART_OVERALL_BAD_SECTOR;
1743 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) bad_attribute_cb, &good) < 0)
1747 *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE;
1751 *overall = SK_SMART_OVERALL_GOOD;
1755 static char* print_name(char *s, size_t len, uint8_t id, const char *k) {
1760 snprintf(s, len, "%u", id);
1767 static char *print_value(char *s, size_t len, uint64_t pretty_value, SkSmartAttributeUnit pretty_unit) {
1769 switch (pretty_unit) {
1770 case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
1772 if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU)
1773 snprintf(s, len, "%0.1f years", ((double) pretty_value)/(1000.0*60*60*24*365));
1774 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU)
1775 snprintf(s, len, "%0.1f months", ((double) pretty_value)/(1000.0*60*60*24*30));
1776 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU)
1777 snprintf(s, len, "%0.1f days", ((double) pretty_value)/(1000.0*60*60*24));
1778 else if (pretty_value >= 1000LLU*60LLU*60LLU)
1779 snprintf(s, len, "%0.1f h", ((double) pretty_value)/(1000.0*60*60));
1780 else if (pretty_value >= 1000LLU*60LLU)
1781 snprintf(s, len, "%0.1f min", ((double) pretty_value)/(1000.0*60));
1782 else if (pretty_value >= 1000LLU)
1783 snprintf(s, len, "%0.1f s", ((double) pretty_value)/(1000.0));
1785 snprintf(s, len, "%llu ms", (unsigned long long) pretty_value);
1789 case SK_SMART_ATTRIBUTE_UNIT_MKELVIN:
1790 snprintf(s, len, "%0.1f C", ((double) pretty_value - 273150) / 1000);
1793 case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
1794 snprintf(s, len, "%llu sectors", (unsigned long long) pretty_value);
1797 case SK_SMART_ATTRIBUTE_UNIT_NONE:
1798 snprintf(s, len, "%llu", (unsigned long long) pretty_value);
1801 case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
1802 snprintf(s, len, "n/a");
1805 case _SK_SMART_ATTRIBUTE_UNIT_MAX:
1814 #define HIGHLIGHT "\x1B[1m"
1815 #define ENDHIGHLIGHT "\x1B[0m"
1817 static void disk_dump_attributes(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) {
1820 char tt[32], tw[32], tc[32];
1823 snprintf(tt, sizeof(tt), "%3u", a->threshold);
1824 tt[sizeof(tt)-1] = 0;
1825 snprintf(tw, sizeof(tw), "%3u", a->worst_value);
1826 tw[sizeof(tw)-1] = 0;
1827 snprintf(tc, sizeof(tc), "%3u", a->current_value);
1828 tc[sizeof(tc)-1] = 0;
1830 highlight = a->good_valid && !a->good && isatty(1);
1833 fprintf(stderr, HIGHLIGHT);
1835 printf("%3u %-27s %-3s %-3s %-3s %-11s 0x%02x%02x%02x%02x%02x%02x %-7s %-7s %-3s\n",
1837 print_name(name, sizeof(name), a->id, a->name),
1838 a->current_value_valid ? tc : "n/a",
1839 a->worst_value_valid ? tw : "n/a",
1840 a->threshold_valid ? tt : "n/a",
1841 print_value(pretty, sizeof(pretty), a->pretty_value, a->pretty_unit),
1842 a->raw[0], a->raw[1], a->raw[2], a->raw[3], a->raw[4], a->raw[5],
1843 a->prefailure ? "prefail" : "old-age",
1844 a->online ? "online" : "offline",
1845 a->good_valid ? yes_no(a->good) : "n/a");
1848 fprintf(stderr, ENDHIGHLIGHT);
1851 int sk_disk_dump(SkDisk *d) {
1853 SkBool awake = FALSE;
1858 printf("Device: %s\n"
1860 d->name ? d->name : "n/a",
1861 disk_type_to_string(d->type));
1863 ret = sk_disk_get_size(d, &size);
1865 printf("Size: %lu MiB\n", (unsigned long) (d->size/1024/1024));
1867 printf("Size: %s\n", strerror(errno));
1869 if (d->identify_valid) {
1870 const SkIdentifyParsedData *ipd;
1871 SkSmartQuirk quirk = 0;
1874 if ((ret = sk_disk_identify_parse(d, &ipd)) < 0)
1877 printf("Model: [%s]\n"
1880 "SMART Available: %s\n",
1884 yes_no(disk_smart_is_available(d)));
1886 if ((ret = lookup_quirks(ipd->model, ipd->firmware, &quirk)))
1891 for (i = 0; quirk_name[i]; i++)
1893 printf(" %s", _P(quirk_name[i]));
1899 ret = sk_disk_check_sleep_mode(d, &awake);
1900 printf("Awake: %s\n",
1901 ret >= 0 ? yes_no(awake) : strerror(errno));
1903 if (disk_smart_is_available(d)) {
1904 SkSmartOverall overall;
1905 const SkSmartParsedData *spd;
1910 ret = sk_disk_smart_status(d, &good);
1911 printf("SMART Disk Health Good: %s\n",
1912 ret >= 0 ? yes_no(good) : strerror(errno));
1914 if ((ret = sk_disk_smart_read_data(d)) < 0)
1917 if ((ret = sk_disk_smart_parse(d, &spd)) < 0)
1920 printf("Off-line Data Collection Status: [%s]\n"
1921 "Total Time To Complete Off-Line Data Collection: %u s\n"
1922 "Self-Test Execution Status: [%s]\n"
1923 "Percent Self-Test Remaining: %u%%\n"
1924 "Conveyance Self-Test Available: %s\n"
1925 "Short/Extended Self-Test Available: %s\n"
1926 "Start Self-Test Available: %s\n"
1927 "Abort Self-Test Available: %s\n"
1928 "Short Self-Test Polling Time: %u min\n"
1929 "Extended Self-Test Polling Time: %u min\n"
1930 "Conveyance Self-Test Polling Time: %u min\n",
1931 sk_smart_offline_data_collection_status_to_string(spd->offline_data_collection_status),
1932 spd->total_offline_data_collection_seconds,
1933 sk_smart_self_test_execution_status_to_string(spd->self_test_execution_status),
1934 spd->self_test_execution_percent_remaining,
1935 yes_no(spd->conveyance_test_available),
1936 yes_no(spd->short_and_extended_test_available),
1937 yes_no(spd->start_test_available),
1938 yes_no(spd->abort_test_available),
1939 spd->short_test_polling_minutes,
1940 spd->extended_test_polling_minutes,
1941 spd->conveyance_test_polling_minutes);
1943 if (sk_disk_smart_get_bad(d, &value) < 0)
1944 printf("Bad Sectors: %s\n", strerror(errno));
1946 printf("%sBad Sectors: %s%s\n",
1947 value > 0 ? HIGHLIGHT : "",
1948 print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_SECTORS),
1949 value > 0 ? ENDHIGHLIGHT : "");
1951 if (sk_disk_smart_get_power_on(d, &value) < 0)
1952 printf("Powered On: %s\n", strerror(errno));
1954 printf("Powered On: %s\n", print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
1956 if (sk_disk_smart_get_temperature(d, &value) < 0)
1957 printf("Temperature: %s\n", strerror(errno));
1959 printf("Temperature: %s\n", print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_MKELVIN));
1961 if (sk_disk_smart_get_overall(d, &overall) < 0)
1962 printf("Overall Status: %s\n", strerror(errno));
1964 printf("%sOverall Status: %s%s\n",
1965 overall != SK_SMART_OVERALL_GOOD ? HIGHLIGHT : "",
1966 sk_smart_overall_to_string(overall),
1967 overall != SK_SMART_OVERALL_GOOD ? ENDHIGHLIGHT : "");
1969 printf("%3s %-27s %5s %5s %5s %-11s %-14s %-7s %-7s %-3s\n",
1981 if ((ret = sk_disk_smart_parse_attributes(d, disk_dump_attributes, NULL)) < 0)
1988 int sk_disk_get_size(SkDisk *d, uint64_t *bytes) {
1992 if (d->size == (uint64_t) -1) {
2001 static int disk_find_type(SkDisk *d, dev_t devnum) {
2003 struct udev_device *dev = NULL;
2009 if (!(udev = udev_new())) {
2014 if (!(dev = udev_device_new_from_devnum(udev, 'b', devnum))) {
2019 if (!(bus = udev_device_get_property_value(dev, "ID_BUS")))
2020 d->type = SK_DISK_TYPE_UNKNOWN;
2022 if (strcmp(bus, "ata") == 0)
2023 d->type = SK_DISK_TYPE_ATA;
2024 else if (strcmp(bus, "scsi") == 0)
2025 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_16;
2026 else if (strcmp(bus, "usb") == 0) {
2027 struct udev_device *usb;
2028 const char *product, *vendor;
2031 if (!(usb = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"))) {
2036 if (!(product = udev_device_get_sysattr_value(usb, "idProduct")) ||
2037 sscanf(product, "%04x", &pid) != 1) {
2042 if (!(vendor = udev_device_get_sysattr_value(usb, "idVendor")) ||
2043 sscanf(vendor, "%04x", &vid) != 1) {
2048 if ((vid == 0x0c0b && pid == 0xb159) ||
2049 (vid == 0x04fc && pid == 0x0c25))
2050 d->type = SK_DISK_TYPE_SUNPLUS;
2052 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_12;
2055 d->type = SK_DISK_TYPE_UNKNOWN;
2062 udev_device_unref(dev);
2070 int sk_disk_open(const char *name, SkDisk **_d) {
2077 if (!(d = calloc(1, sizeof(SkDisk)))) {
2084 d->type = SK_DISK_TYPE_BLOB;
2085 d->size = (uint64_t) -1;
2088 if (!(d->name = strdup(name))) {
2093 if ((d->fd = open(name,
2094 O_RDONLY|O_NOCTTY|O_NONBLOCK
2104 if ((ret = fstat(d->fd, &st)) < 0)
2107 if (!S_ISBLK(st.st_mode)) {
2113 /* So, it's a block device. Let's make sure the ioctls work */
2114 if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0)
2117 if (d->size <= 0 || d->size == (uint64_t) -1) {
2123 /* OK, it's a real block device with a size. Now let's find the suitable API */
2124 if ((ret = disk_find_type(d, st.st_rdev)) < 0)
2127 if (d->type == SK_DISK_TYPE_UNKNOWN) {
2128 /* We have no clue, so let's autotest for a working API */
2129 for (d->type = 0; d->type < _SK_DISK_TYPE_TEST_MAX; d->type++)
2130 if (disk_identify_device(d) >= 0)
2133 disk_identify_device(d);
2135 /* Check if driver can do SMART, and enable if necessary */
2136 if (disk_smart_is_available(d)) {
2138 if (!disk_smart_is_enabled(d)) {
2139 if ((ret = disk_smart_enable(d, TRUE)) < 0)
2142 if ((ret = disk_identify_device(d)) < 0)
2145 if (!disk_smart_is_enabled(d)) {
2152 disk_smart_read_thresholds(d);
2168 void sk_disk_free(SkDisk *d) {
2179 int sk_disk_get_blob(SkDisk *d, const void **blob, size_t *rsize) {
2181 SkBool good, have_good = FALSE;
2189 (d->identify_valid ? 8 + sizeof(d->identify) : 0) +
2190 (d->smart_data_valid ? 8 + sizeof(d->smart_data) : 0) +
2191 (d->smart_thresholds ? 8 + sizeof(d->smart_thresholds) : 0);
2193 if (sk_disk_smart_status(d, &good) >= 0) {
2204 if (!(d->blob = malloc(size))) {
2211 /* These memory accesses are only OK as long as all our
2212 * objects are sensibly aligned, which they are... */
2214 if (d->identify_valid) {
2215 p[0] = SK_BLOB_TAG_IDENTIFY;
2216 p[1] = htonl(sizeof(d->identify));
2219 memcpy(p, d->identify, sizeof(d->identify));
2220 p = (uint32_t*) ((uint8_t*) p + sizeof(d->identify));
2224 p[0] = SK_BLOB_TAG_SMART_STATUS;
2226 p[2] = htonl(!!good);
2230 if (d->smart_data_valid) {
2231 p[0] = SK_BLOB_TAG_SMART_DATA;
2232 p[1] = htonl(sizeof(d->smart_data));
2235 memcpy(p, d->smart_data, sizeof(d->smart_data));
2236 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_data));
2239 if (d->smart_thresholds_valid) {
2240 p[0] = SK_BLOB_TAG_SMART_THRESHOLDS;
2241 p[1] = htonl(sizeof(d->smart_thresholds));
2244 memcpy(p, d->smart_thresholds, sizeof(d->smart_thresholds));
2245 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_thresholds));
2248 assert((size_t) ((uint8_t*) p - (uint8_t*) d->blob) == size);
2256 int sk_disk_set_blob(SkDisk *d, const void *blob, size_t size) {
2259 SkBool idv = FALSE, sdv = FALSE, stv = FALSE, bssv = FALSE;
2264 if (d->type != SK_DISK_TYPE_BLOB) {
2274 /* First run, verify if everything makes sense */
2278 uint32_t tag, tsize;
2286 memcpy(&tsize, p+1, 4);
2290 if (left < ntohl(tsize)) {
2297 case SK_BLOB_TAG_IDENTIFY:
2298 if (ntohl(tsize) != sizeof(d->identify) || idv) {
2305 case SK_BLOB_TAG_SMART_STATUS:
2306 if (ntohl(tsize) != 4 || bssv) {
2313 case SK_BLOB_TAG_SMART_DATA:
2314 if (ntohl(tsize) != sizeof(d->smart_data) || sdv) {
2321 case SK_BLOB_TAG_SMART_THRESHOLDS:
2322 if (ntohl(tsize) != sizeof(d->smart_thresholds) || stv) {
2330 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
2331 left -= ntohl(tsize);
2339 d->identify_valid = idv;
2340 d->smart_data_valid = sdv;
2341 d->smart_thresholds_valid = stv;
2342 d->blob_smart_status_valid = bssv;
2344 /* Second run, actually copy things in */
2348 uint32_t tag, tsize;
2352 memcpy(&tsize, p+1, 4);
2356 assert(left >= ntohl(tsize));
2360 case SK_BLOB_TAG_IDENTIFY:
2361 assert(ntohl(tsize) == sizeof(d->identify));
2362 memcpy(d->identify, p, sizeof(d->identify));
2365 case SK_BLOB_TAG_SMART_STATUS: {
2367 assert(ntohl(tsize) == 4);
2369 d->blob_smart_status = !!ok;
2373 case SK_BLOB_TAG_SMART_DATA:
2374 assert(ntohl(tsize) == sizeof(d->smart_data));
2375 memcpy(d->smart_data, p, sizeof(d->smart_data));
2378 case SK_BLOB_TAG_SMART_THRESHOLDS:
2379 assert(ntohl(tsize) == sizeof(d->smart_thresholds));
2380 memcpy(d->smart_thresholds, p, sizeof(d->smart_thresholds));
2384 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
2385 left -= ntohl(tsize);