1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
4 This file is part of libatasmart.
6 Copyright 2008 Lennart Poettering
8 libatasmart is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as
10 published by the Free Software Foundation, either version 2.1 of the
11 License, or (at your option) any later version.
13 libatasmart is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public
19 License along with libatasmart. If not, If not, see
20 <http://www.gnu.org/licenses/>.
27 #include <arpa/inet.h>
37 #include <sys/ioctl.h>
38 #include <scsi/scsi.h>
40 #include <scsi/scsi_ioctl.h>
41 #include <linux/hdreg.h>
43 #include <sys/types.h>
45 #include <sys/param.h>
54 #define SK_TIMEOUT 2000
56 typedef enum SkDirection {
63 typedef enum SkDiskType {
64 /* These three will be autotested for: */
65 SK_DISK_TYPE_ATA_PASSTHROUGH_12, /* ATA passthrough over SCSI transport, 12-byte version */
66 SK_DISK_TYPE_ATA_PASSTHROUGH_16, /* ATA passthrough over SCSI transport, 16-byte version */
67 SK_DISK_TYPE_LINUX_IDE, /* Classic Linux /dev/hda ioctls */
69 /* These three will not be autotested for */
70 SK_DISK_TYPE_SUNPLUS, /* SunPlus USB/ATA bridges */
71 SK_DISK_TYPE_JMICRON, /* JMicron USB/ATA bridges */
72 SK_DISK_TYPE_BLOB, /* From a file */
73 SK_DISK_TYPE_NONE, /* No access method */
74 SK_DISK_TYPE_AUTO, /* We don't know yet */
76 _SK_DISK_TYPE_TEST_MAX = SK_DISK_TYPE_SUNPLUS /* only auto test until here */
79 #if __BYTE_ORDER == __LITTLE_ENDIAN
80 #define MAKE_TAG(a,b,c,d) \
81 (((uint32_t) d << 24) | \
82 ((uint32_t) c << 16) | \
83 ((uint32_t) b << 8) | \
86 #define MAKE_TAG(a,b,c,d) \
87 (((uint32_t) a << 24) | \
88 ((uint32_t) b << 16) | \
89 ((uint32_t) c << 8) | \
93 typedef enum SkBlobTag {
94 SK_BLOB_TAG_IDENTIFY = MAKE_TAG('I', 'D', 'F', 'Y'),
95 SK_BLOB_TAG_SMART_STATUS = MAKE_TAG('S', 'M', 'S', 'T'),
96 SK_BLOB_TAG_SMART_DATA = MAKE_TAG('S', 'M', 'D', 'T'),
97 SK_BLOB_TAG_SMART_THRESHOLDS = MAKE_TAG('S', 'M', 'T', 'H')
107 uint8_t identify[512];
108 uint8_t smart_data[512];
109 uint8_t smart_thresholds[512];
111 SkBool smart_initialized:1;
113 SkBool identify_valid:1;
114 SkBool smart_data_valid:1;
115 SkBool smart_thresholds_valid:1;
117 SkBool blob_smart_status:1;
118 SkBool blob_smart_status_valid:1;
120 SkBool attribute_verification_bad:1;
122 SkIdentifyParsedData identify_parsed_data;
123 SkSmartParsedData smart_parsed_data;
125 /* cache for commonly used attributes */
126 SkBool attribute_cache_valid:1;
127 SkBool bad_attribute_now:1;
128 SkBool bad_attribute_in_the_past:1;
129 SkBool reallocated_sector_count_found:1;
130 SkBool current_pending_sector_found:1;
131 uint64_t reallocated_sector_count;
132 uint64_t current_pending_sector;
138 typedef enum SkAtaCommand {
139 SK_ATA_COMMAND_IDENTIFY_DEVICE = 0xEC,
140 SK_ATA_COMMAND_IDENTIFY_PACKET_DEVICE = 0xA1,
141 SK_ATA_COMMAND_SMART = 0xB0,
142 SK_ATA_COMMAND_CHECK_POWER_MODE = 0xE5
145 /* ATA SMART subcommands (ATA8 7.52.1) */
146 typedef enum SkSmartCommand {
147 SK_SMART_COMMAND_READ_DATA = 0xD0,
148 SK_SMART_COMMAND_READ_THRESHOLDS = 0xD1,
149 SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE = 0xD4,
150 SK_SMART_COMMAND_ENABLE_OPERATIONS = 0xD8,
151 SK_SMART_COMMAND_DISABLE_OPERATIONS = 0xD9,
152 SK_SMART_COMMAND_RETURN_STATUS = 0xDA
155 /* Hmm, if the data we parse is out of a certain range just consider it misparsed */
156 #define SK_MKELVIN_VALID_MIN ((uint64_t) ((-15LL*1000LL) + 273150LL))
157 #define SK_MKELVIN_VALID_MAX ((uint64_t) ((100LL*1000LL) + 273150LL))
159 #define SK_MSECOND_VALID_MIN 1ULL
160 #define SK_MSECOND_VALID_SHORT_MAX (60ULL * 60ULL * 1000ULL)
161 #define SK_MSECOND_VALID_LONG_MAX (30ULL * 365ULL * 24ULL * 60ULL * 60ULL * 1000ULL)
163 static int init_smart(SkDisk *d);
165 static const char *disk_type_to_human_string(SkDiskType type) {
167 /* %STRINGPOOLSTART% */
168 static const char* const map[_SK_DISK_TYPE_MAX] = {
169 [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = "16 Byte SCSI ATA SAT Passthru",
170 [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = "12 Byte SCSI ATA SAT Passthru",
171 [SK_DISK_TYPE_LINUX_IDE] = "Native Linux IDE",
172 [SK_DISK_TYPE_SUNPLUS] = "Sunplus SCSI ATA Passthru",
173 [SK_DISK_TYPE_JMICRON] = "JMicron SCSI ATA Passthru",
174 [SK_DISK_TYPE_BLOB] = "Blob",
175 [SK_DISK_TYPE_AUTO] = "Automatic",
176 [SK_DISK_TYPE_NONE] = "None"
178 /* %STRINGPOOLSTOP% */
180 if (type >= _SK_DISK_TYPE_MAX)
183 return _P(map[type]);
186 static const char *disk_type_to_prefix_string(SkDiskType type) {
188 /* %STRINGPOOLSTART% */
189 static const char* const map[_SK_DISK_TYPE_MAX] = {
190 [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = "sat16",
191 [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = "sat12",
192 [SK_DISK_TYPE_LINUX_IDE] = "linux-ide",
193 [SK_DISK_TYPE_SUNPLUS] = "sunplus",
194 [SK_DISK_TYPE_JMICRON] = "jmicron",
195 [SK_DISK_TYPE_NONE] = "none",
196 [SK_DISK_TYPE_AUTO] = "auto",
198 /* %STRINGPOOLSTOP% */
200 if (type >= _SK_DISK_TYPE_MAX)
203 return _P(map[type]);
206 static const char *disk_type_from_string(const char *s, SkDiskType *type) {
212 for (u = 0; u < _SK_DISK_TYPE_MAX; u++) {
216 if (!(t = disk_type_to_prefix_string(u)))
221 if (strncmp(s, t, l))
235 static SkBool disk_smart_is_available(SkDisk *d) {
236 return d->identify_valid && !!(d->identify[164] & 1);
239 static SkBool disk_smart_is_enabled(SkDisk *d) {
240 return d->identify_valid && !!(d->identify[170] & 1);
243 static SkBool disk_smart_is_conveyance_test_available(SkDisk *d) {
244 assert(d->smart_data_valid);
246 return !!(d->smart_data[367] & 32);
248 static SkBool disk_smart_is_short_and_extended_test_available(SkDisk *d) {
249 assert(d->smart_data_valid);
251 return !!(d->smart_data[367] & 16);
254 static SkBool disk_smart_is_start_test_available(SkDisk *d) {
255 assert(d->smart_data_valid);
257 return !!(d->smart_data[367] & 1);
260 static SkBool disk_smart_is_abort_test_available(SkDisk *d) {
261 assert(d->smart_data_valid);
263 return !!(d->smart_data[367] & 41);
266 static int disk_linux_ide_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
267 uint8_t *bytes = cmd_data;
270 assert(d->type == SK_DISK_TYPE_LINUX_IDE);
274 case SK_DIRECTION_OUT:
276 /* We could use HDIO_DRIVE_TASKFILE here, but
277 * that's a deprecated ioctl(), hence we don't
278 * do it. And we don't need writing anyway. */
283 case SK_DIRECTION_IN: {
286 /* We have HDIO_DRIVE_CMD which can only read, but not write,
287 * and cannot do LBA. We use it for all read commands. */
289 ioctl_data = alloca(4 + *len);
290 memset(ioctl_data, 0, 4 + *len);
292 ioctl_data[0] = (uint8_t) command; /* COMMAND */
293 ioctl_data[1] = ioctl_data[0] == WIN_SMART ? bytes[9] : bytes[3]; /* SECTOR/NSECTOR */
294 ioctl_data[2] = bytes[1]; /* FEATURE */
295 ioctl_data[3] = bytes[3]; /* NSECTOR */
297 if ((ret = ioctl(d->fd, HDIO_DRIVE_CMD, ioctl_data)) < 0)
300 memset(bytes, 0, 12);
301 bytes[11] = ioctl_data[0];
302 bytes[1] = ioctl_data[1];
303 bytes[3] = ioctl_data[2];
305 memcpy(data, ioctl_data+4, *len);
310 case SK_DIRECTION_NONE: {
311 uint8_t ioctl_data[7];
313 /* We have HDIO_DRIVE_TASK which can neither read nor
314 * write, but can do LBA. We use it for all commands that
315 * do neither read nor write */
317 memset(ioctl_data, 0, sizeof(ioctl_data));
319 ioctl_data[0] = (uint8_t) command; /* COMMAND */
320 ioctl_data[1] = bytes[1]; /* FEATURE */
321 ioctl_data[2] = bytes[3]; /* NSECTOR */
323 ioctl_data[3] = bytes[9]; /* LBA LOW */
324 ioctl_data[4] = bytes[8]; /* LBA MID */
325 ioctl_data[5] = bytes[7]; /* LBA HIGH */
326 ioctl_data[6] = bytes[10]; /* SELECT */
328 if ((ret = ioctl(d->fd, HDIO_DRIVE_TASK, ioctl_data)))
331 memset(bytes, 0, 12);
332 bytes[11] = ioctl_data[0];
333 bytes[1] = ioctl_data[1];
334 bytes[3] = ioctl_data[2];
336 bytes[9] = ioctl_data[3];
337 bytes[8] = ioctl_data[4];
338 bytes[7] = ioctl_data[5];
340 bytes[10] = ioctl_data[6];
351 /* Sends a SCSI command block */
352 static int sg_io(int fd, int direction,
353 const void *cdb, size_t cdb_len,
354 void *data, size_t data_len,
355 void *sense, size_t sense_len) {
357 struct sg_io_hdr io_hdr;
359 memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
361 io_hdr.interface_id = 'S';
362 io_hdr.cmdp = (unsigned char*) cdb;
363 io_hdr.cmd_len = cdb_len;
364 io_hdr.dxferp = data;
365 io_hdr.dxfer_len = data_len;
367 io_hdr.mx_sb_len = sense_len;
368 io_hdr.dxfer_direction = direction;
369 io_hdr.timeout = SK_TIMEOUT;
371 return ioctl(fd, SG_IO, &io_hdr);
374 static int disk_passthrough_16_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
375 uint8_t *bytes = cmd_data;
378 uint8_t *desc = sense+8;
381 static const int direction_map[] = {
382 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
383 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
384 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
387 assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_16);
389 /* ATA Pass-Through 16 byte command, as described in "T10 04-262r8
390 * ATA Command Pass-Through":
391 * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */
393 memset(cdb, 0, sizeof(cdb));
395 cdb[0] = 0x85; /* OPERATION CODE: 16 byte pass through */
397 if (direction == SK_DIRECTION_NONE) {
398 cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */
399 cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */
401 } else if (direction == SK_DIRECTION_IN) {
402 cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
403 cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
405 } else if (direction == SK_DIRECTION_OUT) {
406 cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */
407 cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */
410 cdb[3] = bytes[0]; /* FEATURES */
413 cdb[5] = bytes[2]; /* SECTORS */
416 cdb[8] = bytes[9]; /* LBA LOW */
417 cdb[10] = bytes[8]; /* LBA MID */
418 cdb[12] = bytes[7]; /* LBA HIGH */
420 cdb[13] = bytes[10] & 0x4F; /* SELECT */
421 cdb[14] = (uint8_t) command;
423 memset(sense, 0, sizeof(sense));
425 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
428 if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) {
433 memset(bytes, 0, 12);
441 bytes[10] = desc[12];
442 bytes[11] = desc[13];
447 static int disk_passthrough_12_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
448 uint8_t *bytes = cmd_data;
451 uint8_t *desc = sense+8;
454 static const int direction_map[] = {
455 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
456 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
457 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
460 assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12);
462 /* ATA Pass-Through 12 byte command, as described in "T10 04-262r8
463 * ATA Command Pass-Through":
464 * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */
466 memset(cdb, 0, sizeof(cdb));
468 cdb[0] = 0xa1; /* OPERATION CODE: 12 byte pass through */
470 if (direction == SK_DIRECTION_NONE) {
471 cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */
472 cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */
474 } else if (direction == SK_DIRECTION_IN) {
475 cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
476 cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
478 } else if (direction == SK_DIRECTION_OUT) {
479 cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */
480 cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */
483 cdb[3] = bytes[1]; /* FEATURES */
484 cdb[4] = bytes[3]; /* SECTORS */
486 cdb[5] = bytes[9]; /* LBA LOW */
487 cdb[6] = bytes[8]; /* LBA MID */
488 cdb[7] = bytes[7]; /* LBA HIGH */
490 cdb[8] = bytes[10] & 0x4F; /* SELECT */
491 cdb[9] = (uint8_t) command;
493 memset(sense, 0, sizeof(sense));
495 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
498 if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) {
503 memset(bytes, 0, 12);
505 bytes[1] = desc[3]; /* FEATURES */
506 bytes[2] = desc[4]; /* STATUS */
507 bytes[3] = desc[5]; /* SECTORS */
508 bytes[9] = desc[7]; /* LBA LOW */
509 bytes[8] = desc[9]; /* LBA MID */
510 bytes[7] = desc[11]; /* LBA HIGH */
511 bytes[10] = desc[12]; /* SELECT */
512 bytes[11] = desc[13]; /* ERROR */
517 static int disk_sunplus_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
518 uint8_t *bytes = cmd_data;
520 uint8_t sense[32], buf[8];
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_SUNPLUS);
530 /* SunplusIT specific SCSI ATA pass-thru. Inspired by smartmonutils' support for these bridges */
532 memset(cdb, 0, sizeof(cdb));
534 cdb[0] = 0xF8; /* OPERATION CODE: Sunplus specific */
535 cdb[1] = 0x00; /* Subcommand: Pass-thru */
538 if (direction == SK_DIRECTION_NONE)
539 cdb[3] = 0x00; /* protocol */
540 else if (direction == SK_DIRECTION_IN)
541 cdb[3] = 0x10; /* protocol */
542 else if (direction == SK_DIRECTION_OUT)
543 cdb[3] = 0x11; /* protocol */
545 cdb[4] = bytes[3]; /* size? */
546 cdb[5] = bytes[1]; /* FEATURES */
547 cdb[6] = bytes[3]; /* SECTORS */
548 cdb[7] = bytes[9]; /* LBA LOW */
549 cdb[8] = bytes[8]; /* LBA MID */
550 cdb[9] = bytes[7]; /* LBA HIGH */
551 cdb[10] = bytes[10] | 0xA0; /* SELECT */
552 cdb[11] = (uint8_t) command;
554 memset(sense, 0, sizeof(sense));
557 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
560 memset(cdb, 0, sizeof(cdb));
566 memset(buf, 0, sizeof(buf));
568 /* Ask for response */
569 if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), buf, sizeof(buf), sense, sizeof(sense))) < 0)
572 memset(bytes, 0, 12);
574 bytes[2] = buf[1]; /* ERROR */
575 bytes[3] = buf[2]; /* SECTORS */
576 bytes[9] = buf[3]; /* LBA LOW */
577 bytes[8] = buf[4]; /* LBA MID */
578 bytes[7] = buf[5]; /* LBA HIGH */
579 bytes[10] = buf[6]; /* SELECT */
580 bytes[11] = buf[7]; /* STATUS */
585 static int disk_jmicron_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* _data, size_t *_len) {
586 uint8_t *bytes = cmd_data;
591 SkBool is_smart_status = FALSE;
593 size_t len = _len ? *_len : 0;
594 uint8_t smart_status = 0;
596 static const int direction_map[] = {
597 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
598 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
599 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
602 assert(d->type == SK_DISK_TYPE_JMICRON);
604 /* JMicron specific SCSI ATA pass-thru. Inspired by smartmonutils' support for these bridges */
606 memset(cdb, 0, sizeof(cdb));
608 cdb[0] = 0xdf; /* operation code */
611 cdb[3] = 0x00; /* size HI */
612 cdb[4] = sizeof(port); /* size LO */
614 cdb[6] = 0x72; /* register address HI */
615 cdb[7] = 0x0f; /* register address LO */
621 memset(sense, 0, sizeof(sense));
623 if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), &port, sizeof(port), sense, sizeof(sense))) < 0)
626 /* Port & 0x04 is port #0, Port & 0x40 is port #1 */
630 cdb[0] = 0xdf; /* OPERATION CODE: 12 byte pass through */
632 if (command == SK_ATA_COMMAND_SMART && bytes[1] == SK_SMART_COMMAND_RETURN_STATUS) {
633 /* We need to rewrite the SMART status request */
634 is_smart_status = TRUE;
635 direction = SK_DIRECTION_IN;
636 data = &smart_status;
637 len = sizeof(smart_status);
639 } else if (direction == SK_DIRECTION_NONE)
641 else if (direction == SK_DIRECTION_IN)
643 else if (direction == SK_DIRECTION_OUT)
648 cdb[3] = (uint8_t) (len >> 8);
649 cdb[4] = (uint8_t) (len & 0xFF);
651 cdb[5] = bytes[1]; /* FEATURES */
652 cdb[6] = bytes[3]; /* SECTORS */
654 cdb[7] = bytes[9]; /* LBA LOW */
655 cdb[8] = bytes[8]; /* LBA MID */
656 cdb[9] = bytes[7]; /* LBA HIGH */
658 cdb[10] = bytes[10] | ((port & 0x04) ? 0xA0 : 0xB0); /* SELECT */
659 cdb[11] = (uint8_t) command;
661 memset(sense, 0, sizeof(sense));
663 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len, sense, sizeof(sense))) < 0)
666 memset(bytes, 0, 12);
668 if (is_smart_status) {
669 if (smart_status == 0x01 || smart_status == 0xc2) {
670 bytes[7] = 0xc2; /* LBA HIGH */
671 bytes[8] = 0x4f; /* LBA MID */
672 } else if (smart_status == 0x00 || smart_status == 0x2c) {
673 bytes[7] = 0x2c; /* LBA HIGH */
674 bytes[8] = 0xf4; /* LBA MID */
680 cdb[0] = 0xdf; /* operation code */
683 cdb[3] = 0x00; /* size HI */
684 cdb[4] = sizeof(regbuf); /* size LO */
686 cdb[6] = (port & 0x04) ? 0x80 : 0x90; /* register address HI */
687 cdb[7] = 0x00; /* register address LO */
693 if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), regbuf, sizeof(regbuf), sense, sizeof(sense))) < 0)
696 bytes[2] = regbuf[14]; /* STATUS */
697 bytes[3] = regbuf[0]; /* SECTORS */
698 bytes[9] = regbuf[6]; /* LBA LOW */
699 bytes[8] = regbuf[4]; /* LBA MID */
700 bytes[7] = regbuf[10]; /* LBA HIGH */
701 bytes[10] = regbuf[9]; /* SELECT */
702 bytes[11] = regbuf[13]; /* ERROR */
708 static int disk_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
710 static int (* const disk_command_table[_SK_DISK_TYPE_MAX]) (SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) = {
711 [SK_DISK_TYPE_LINUX_IDE] = disk_linux_ide_command,
712 [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = disk_passthrough_12_command,
713 [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = disk_passthrough_16_command,
714 [SK_DISK_TYPE_SUNPLUS] = disk_sunplus_command,
715 [SK_DISK_TYPE_JMICRON] = disk_jmicron_command,
716 [SK_DISK_TYPE_BLOB] = NULL,
717 [SK_DISK_TYPE_AUTO] = NULL,
718 [SK_DISK_TYPE_NONE] = NULL
722 assert(d->type <= _SK_DISK_TYPE_MAX);
723 assert(direction <= _SK_DIRECTION_MAX);
725 assert(direction == SK_DIRECTION_NONE || (data && len && *len > 0));
726 assert(direction != SK_DIRECTION_NONE || (!data && !len));
728 if (!disk_command_table[d->type]) {
733 return disk_command_table[d->type](d, command, direction, cmd_data, data, len);
736 static int disk_identify_device(SkDisk *d) {
742 if (d->type == SK_DISK_TYPE_BLOB)
745 memset(d->identify, 0, len);
746 memset(cmd, 0, sizeof(cmd));
750 if ((ret = disk_command(d, SK_ATA_COMMAND_IDENTIFY_DEVICE, SK_DIRECTION_IN, cmd, d->identify, &len)) < 0)
758 /* Check if IDENTIFY data is all NULs */
759 for (p = d->identify; p < (const uint8_t*) d->identify+len; p++)
770 d->identify_valid = TRUE;
775 int sk_disk_check_sleep_mode(SkDisk *d, SkBool *awake) {
780 if (!d->identify_valid) {
785 if (d->type == SK_DISK_TYPE_BLOB) {
790 memset(cmd, 0, sizeof(cmd));
792 if ((ret = disk_command(d, SK_ATA_COMMAND_CHECK_POWER_MODE, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
795 if (cmd[0] != 0 || (ntohs(cmd[5]) & 1) != 0) {
800 status = ntohs(cmd[1]) & 0xFF;
801 *awake = status == 0xFF || status == 0x80; /* idle and active/idle is considered awake */
806 static int disk_smart_enable(SkDisk *d, SkBool b) {
809 if (!disk_smart_is_available(d)) {
814 if (d->type == SK_DISK_TYPE_BLOB) {
819 memset(cmd, 0, sizeof(cmd));
821 cmd[0] = htons(b ? SK_SMART_COMMAND_ENABLE_OPERATIONS : SK_SMART_COMMAND_DISABLE_OPERATIONS);
822 cmd[2] = htons(0x0000U);
823 cmd[3] = htons(0x00C2U);
824 cmd[4] = htons(0x4F00U);
826 return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0);
829 int sk_disk_smart_read_data(SkDisk *d) {
834 if (init_smart(d) < 0)
837 if (!disk_smart_is_available(d)) {
842 if (d->type == SK_DISK_TYPE_BLOB)
845 memset(cmd, 0, sizeof(cmd));
847 cmd[0] = htons(SK_SMART_COMMAND_READ_DATA);
849 cmd[2] = htons(0x0000U);
850 cmd[3] = htons(0x00C2U);
851 cmd[4] = htons(0x4F00U);
853 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_data, &len)) < 0)
856 d->smart_data_valid = TRUE;
861 static int disk_smart_read_thresholds(SkDisk *d) {
866 if (!disk_smart_is_available(d)) {
871 if (d->type == SK_DISK_TYPE_BLOB)
874 memset(cmd, 0, sizeof(cmd));
876 cmd[0] = htons(SK_SMART_COMMAND_READ_THRESHOLDS);
878 cmd[2] = htons(0x0000U);
879 cmd[3] = htons(0x00C2U);
880 cmd[4] = htons(0x4F00U);
882 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_thresholds, &len)) < 0)
885 d->smart_thresholds_valid = TRUE;
890 int sk_disk_smart_status(SkDisk *d, SkBool *good) {
894 if (init_smart(d) < 0)
897 if (!disk_smart_is_available(d)) {
902 if (d->type == SK_DISK_TYPE_BLOB) {
904 if (d->blob_smart_status_valid) {
905 *good = d->blob_smart_status;
913 memset(cmd, 0, sizeof(cmd));
915 cmd[0] = htons(SK_SMART_COMMAND_RETURN_STATUS);
916 cmd[1] = htons(0x0000U);
917 cmd[3] = htons(0x00C2U);
918 cmd[4] = htons(0x4F00U);
920 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
923 /* SAT/USB bridges truncate packets, so we only check for 4F,
924 * not for 2C on those */
925 if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x00C2U)) &&
926 cmd[4] == htons(0x4F00U))
928 else if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x002CU)) &&
929 cmd[4] == htons(0xF400U))
939 int sk_disk_smart_self_test(SkDisk *d, SkSmartSelfTest test) {
943 if (init_smart(d) < 0)
946 if (!disk_smart_is_available(d)) {
951 if (d->type == SK_DISK_TYPE_BLOB) {
956 if (!d->smart_data_valid)
957 if ((ret = sk_disk_smart_read_data(d)) < 0)
960 assert(d->smart_data_valid);
962 if (test != SK_SMART_SELF_TEST_SHORT &&
963 test != SK_SMART_SELF_TEST_EXTENDED &&
964 test != SK_SMART_SELF_TEST_CONVEYANCE &&
965 test != SK_SMART_SELF_TEST_ABORT) {
970 if (!disk_smart_is_start_test_available(d)
971 || (test == SK_SMART_SELF_TEST_ABORT && !disk_smart_is_abort_test_available(d))
972 || ((test == SK_SMART_SELF_TEST_SHORT || test == SK_SMART_SELF_TEST_EXTENDED) && !disk_smart_is_short_and_extended_test_available(d))
973 || (test == SK_SMART_SELF_TEST_CONVEYANCE && !disk_smart_is_conveyance_test_available(d))) {
978 if (test == SK_SMART_SELF_TEST_ABORT &&
979 !disk_smart_is_abort_test_available(d)) {
984 memset(cmd, 0, sizeof(cmd));
986 cmd[0] = htons(SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE);
987 cmd[2] = htons(0x0000U);
988 cmd[3] = htons(0x00C2U);
989 cmd[4] = htons(0x4F00U | (uint16_t) test);
991 return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, NULL);
994 static void swap_strings(char *s, size_t len) {
995 assert((len & 1) == 0);
997 for (; len > 0; s += 2, len -= 2) {
1005 static void clean_strings(char *s) {
1008 for (e = s; *e; e++)
1009 if (*e < ' ' || *e >= 127)
1013 static void drop_spaces(char *s) {
1015 SkBool prev_space = FALSE;
1017 s += strspn(s, " ");
1038 static void read_string(char *d, uint8_t *s, size_t len) {
1041 swap_strings(d, len);
1046 int sk_disk_identify_parse(SkDisk *d, const SkIdentifyParsedData **ipd) {
1050 if (!d->identify_valid) {
1055 read_string(d->identify_parsed_data.serial, d->identify+20, 20);
1056 read_string(d->identify_parsed_data.firmware, d->identify+46, 8);
1057 read_string(d->identify_parsed_data.model, d->identify+54, 40);
1059 *ipd = &d->identify_parsed_data;
1064 int sk_disk_smart_is_available(SkDisk *d, SkBool *b) {
1068 if (!d->identify_valid) {
1073 *b = disk_smart_is_available(d);
1077 int sk_disk_identify_is_available(SkDisk *d, SkBool *b) {
1081 *b = d->identify_valid;
1085 const char *sk_smart_offline_data_collection_status_to_string(SkSmartOfflineDataCollectionStatus status) {
1087 /* %STRINGPOOLSTART% */
1088 static const char* const map[] = {
1089 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER] = "Off-line data collection activity was never started.",
1090 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS] = "Off-line data collection activity was completed without error.",
1091 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS] = "Off-line activity in progress.",
1092 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED] = "Off-line data collection activity was suspended by an interrupting command from host.",
1093 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED] = "Off-line data collection activity was aborted by an interrupting command from host.",
1094 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL] = "Off-line data collection activity was aborted by the device with a fatal error.",
1095 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN] = "Unknown status"
1097 /* %STRINGPOOLSTOP% */
1099 if (status >= _SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_MAX)
1102 return _P(map[status]);
1105 const char *sk_smart_self_test_execution_status_to_string(SkSmartSelfTestExecutionStatus status) {
1107 /* %STRINGPOOLSTART% */
1108 static const char* const map[] = {
1109 [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.",
1110 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED] = "The self-test routine was aborted by the host.",
1111 [SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED] = "The self-test routine was interrupted by the host with a hardware or software reset.",
1112 [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.",
1113 [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.",
1114 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL] = "The previous self-test completed having the electrical element of the test failed.",
1115 [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.",
1116 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ] = "The previous self-test completed having the read element of the test failed.",
1117 [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.",
1118 [SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS] = "Self-test routine in progress"
1120 /* %STRINGPOOLSTOP% */
1122 if (status >= _SK_SMART_SELF_TEST_EXECUTION_STATUS_MAX)
1125 return _P(map[status]);
1128 const char* sk_smart_self_test_to_string(SkSmartSelfTest test) {
1131 case SK_SMART_SELF_TEST_SHORT:
1133 case SK_SMART_SELF_TEST_EXTENDED:
1135 case SK_SMART_SELF_TEST_CONVEYANCE:
1136 return "conveyance";
1137 case SK_SMART_SELF_TEST_ABORT:
1144 SkBool sk_smart_self_test_available(const SkSmartParsedData *d, SkSmartSelfTest test) {
1147 if (!d->start_test_available)
1151 case SK_SMART_SELF_TEST_SHORT:
1152 case SK_SMART_SELF_TEST_EXTENDED:
1153 return d->short_and_extended_test_available;
1154 case SK_SMART_SELF_TEST_CONVEYANCE:
1155 return d->conveyance_test_available;
1156 case SK_SMART_SELF_TEST_ABORT:
1157 return d->abort_test_available;
1163 unsigned sk_smart_self_test_polling_minutes(const SkSmartParsedData *d, SkSmartSelfTest test) {
1166 if (!sk_smart_self_test_available(d, test))
1170 case SK_SMART_SELF_TEST_SHORT:
1171 return d->short_test_polling_minutes;
1172 case SK_SMART_SELF_TEST_EXTENDED:
1173 return d->extended_test_polling_minutes;
1174 case SK_SMART_SELF_TEST_CONVEYANCE:
1175 return d->conveyance_test_polling_minutes;
1181 static void make_pretty(SkSmartAttributeParsedData *a) {
1182 uint64_t fourtyeight;
1187 if (a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_UNKNOWN)
1191 ((uint64_t) a->raw[0]) |
1192 (((uint64_t) a->raw[1]) << 8) |
1193 (((uint64_t) a->raw[2]) << 16) |
1194 (((uint64_t) a->raw[3]) << 24) |
1195 (((uint64_t) a->raw[4]) << 32) |
1196 (((uint64_t) a->raw[5]) << 40);
1198 if (!strcmp(a->name, "spin-up-time"))
1199 a->pretty_value = fourtyeight & 0xFFFF;
1200 else if (!strcmp(a->name, "airflow-temperature-celsius") ||
1201 !strcmp(a->name, "temperature-celsius") ||
1202 !strcmp(a->name, "temperature-celsius-2"))
1203 a->pretty_value = (fourtyeight & 0xFFFF)*1000 + 273150;
1204 else if (!strcmp(a->name, "temperature-centi-celsius"))
1205 a->pretty_value = (fourtyeight & 0xFFFF)*100 + 273150;
1206 else if (!strcmp(a->name, "power-on-minutes"))
1207 a->pretty_value = fourtyeight * 60 * 1000;
1208 else if (!strcmp(a->name, "power-on-seconds") ||
1209 !strcmp(a->name, "power-on-seconds-2"))
1210 a->pretty_value = fourtyeight * 1000;
1211 else if (!strcmp(a->name, "power-on-half-minutes"))
1212 a->pretty_value = fourtyeight * 30 * 1000;
1213 else if (!strcmp(a->name, "power-on-hours") ||
1214 !strcmp(a->name, "loaded-hours") ||
1215 !strcmp(a->name, "head-flying-hours"))
1216 a->pretty_value = (fourtyeight & 0xFFFFFFFFU) * 60 * 60 * 1000;
1217 else if (!strcmp(a->name, "reallocated-sector-count") ||
1218 !strcmp(a->name, "current-pending-sector"))
1219 a->pretty_value = fourtyeight & 0xFFFFFFFFU;
1220 else if (!strcmp(a->name, "endurance-remaining") ||
1221 !strcmp(a->name, "available-reserved-space"))
1222 a->pretty_value = a->current_value;
1223 else if (!strcmp(a->name, "total-lbas-written") ||
1224 !strcmp(a->name, "total-lbas-read"))
1225 a->pretty_value = fourtyeight * 65536LLU * 512LLU / 1000000LLU;
1226 else if (!strcmp(a->name, "timed-workload-media-wear") ||
1227 !strcmp(a->name, "timed-workload-host-reads"))
1228 a->pretty_value = (double)fourtyeight / 1024LLU;
1229 else if (!strcmp(a->name, "workload-timer"))
1230 a->pretty_value = fourtyeight * 60 * 1000;
1232 a->pretty_value = fourtyeight;
1235 typedef void (*SkSmartAttributeVerify)(SkDisk *d, SkSmartAttributeParsedData *a);
1237 typedef struct SkSmartAttributeInfo {
1239 SkSmartAttributeUnit unit;
1240 SkSmartAttributeVerify verify;
1241 } SkSmartAttributeInfo;
1243 static void verify_temperature(SkDisk *d, SkSmartAttributeParsedData *a) {
1245 assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_MKELVIN);
1247 if (a->pretty_value < SK_MKELVIN_VALID_MIN ||
1248 a->pretty_value > SK_MKELVIN_VALID_MAX) {
1249 a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1250 d->attribute_verification_bad = TRUE;
1254 static void verify_short_time(SkDisk *d, SkSmartAttributeParsedData *a) {
1256 assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_MSECONDS);
1258 if (a->pretty_value < SK_MSECOND_VALID_MIN ||
1259 a->pretty_value > SK_MSECOND_VALID_SHORT_MAX) {
1260 a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1261 d->attribute_verification_bad = TRUE;
1265 static void verify_long_time(SkDisk *d, SkSmartAttributeParsedData *a) {
1267 assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_MSECONDS);
1269 if (a->pretty_value < SK_MSECOND_VALID_MIN ||
1270 a->pretty_value > SK_MSECOND_VALID_LONG_MAX) {
1271 a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1272 d->attribute_verification_bad = TRUE;
1276 static void verify_sectors(SkDisk *d, SkSmartAttributeParsedData *a) {
1277 uint64_t max_sectors;
1281 assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_SECTORS);
1283 max_sectors = d->size / 512ULL;
1285 if (a->pretty_value == 0xffffffffULL ||
1286 a->pretty_value == 0xffffffffffffULL ||
1287 (max_sectors > 0 && a->pretty_value > max_sectors)) {
1288 a->pretty_value = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1289 d->attribute_verification_bad = TRUE;
1291 if ((!strcmp(a->name, "reallocated-sector-count") ||
1292 !strcmp(a->name, "current-pending-sector")) &&
1293 a->pretty_value > 0)
1298 /* This data is stolen from smartmontools */
1300 /* %STRINGPOOLSTART% */
1301 static const SkSmartAttributeInfo const attribute_info[256] = {
1302 [1] = { "raw-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1303 [2] = { "throughput-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1304 [3] = { "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_short_time },
1305 [4] = { "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1306 [5] = { "reallocated-sector-count", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1307 [6] = { "read-channel-margin", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1308 [7] = { "seek-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1309 [8] = { "seek-time-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1310 [9] = { "power-on-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1311 [10] = { "spin-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1312 [11] = { "calibration-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1313 [12] = { "power-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1314 [13] = { "read-soft-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1315 [170] = { "available-reserved-space", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL },
1316 [171] = { "program-fail-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1317 [172] = { "erase-fail-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1318 [184] = { "end-to-end-error", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1319 [187] = { "reported-uncorrect", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1320 [188] = { "command-timeout", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1321 [189] = { "high-fly-writes", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1322 [190] = { "airflow-temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1323 [191] = { "g-sense-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1324 [192] = { "power-off-retract-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1325 [193] = { "load-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1326 [194] = { "temperature-celsius-2", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1327 [195] = { "hardware-ecc-recovered", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1328 [196] = { "reallocated-event-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1329 [197] = { "current-pending-sector", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1330 [198] = { "offline-uncorrectable", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1331 [199] = { "udma-crc-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1332 [200] = { "multi-zone-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1333 [201] = { "soft-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1334 [202] = { "ta-increase-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1335 [203] = { "run-out-cancel", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1336 [204] = { "shock-count-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1337 [205] = { "shock-rate-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1338 [206] = { "flying-height", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1339 [207] = { "spin-high-current", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1340 [208] = { "spin-buzz", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1341 [209] = { "offline-seek-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1342 [220] = { "disk-shift", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1343 [221] = { "g-sense-error-rate-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1344 [222] = { "loaded-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1345 [223] = { "load-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1346 [224] = { "load-friction", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1347 [225] = { "load-cycle-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1348 [226] = { "load-in-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_short_time },
1349 [227] = { "torq-amp-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1350 [228] = { "power-off-retract-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1351 [230] = { "head-amplitude", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1352 [231] = { "temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1354 /* http://www.adtron.com/pdf/SMART_for_XceedLite_SATA_RevA.pdf */
1355 [232] = { "endurance-remaining", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL },
1356 [233] = { "power-on-seconds-2", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1357 [234] = { "uncorrectable-ecc-count", SK_SMART_ATTRIBUTE_UNIT_SECTORS, NULL },
1358 [235] = { "good-block-rate", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1360 [240] = { "head-flying-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1361 [241] = { "total-lbas-written", SK_SMART_ATTRIBUTE_UNIT_MB, NULL },
1362 [242] = { "total-lbas-read", SK_SMART_ATTRIBUTE_UNIT_MB, NULL },
1363 [250] = { "read-error-retry-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }
1365 /* %STRINGPOOLSTOP% */
1367 typedef enum SkSmartQuirk {
1368 SK_SMART_QUIRK_9_POWERONMINUTES = 0x000001,
1369 SK_SMART_QUIRK_9_POWERONSECONDS = 0x000002,
1370 SK_SMART_QUIRK_9_POWERONHALFMINUTES = 0x000004,
1371 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT = 0x000008,
1372 SK_SMART_QUIRK_193_LOADUNLOAD = 0x000010,
1373 SK_SMART_QUIRK_194_10XCELSIUS = 0x000020,
1374 SK_SMART_QUIRK_194_UNKNOWN = 0x000040,
1375 SK_SMART_QUIRK_200_WRITEERRORCOUNT = 0x000080,
1376 SK_SMART_QUIRK_201_DETECTEDTACOUNT = 0x000100,
1377 SK_SMART_QUIRK_5_UNKNOWN = 0x000200,
1378 SK_SMART_QUIRK_9_UNKNOWN = 0x000400,
1379 SK_SMART_QUIRK_197_UNKNOWN = 0x000800,
1380 SK_SMART_QUIRK_198_UNKNOWN = 0x001000,
1381 SK_SMART_QUIRK_190_UNKNOWN = 0x002000,
1382 SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE = 0x004000,
1383 SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR = 0x008000,
1384 SK_SMART_QUIRK_225_TOTALLBASWRITTEN = 0x010000,
1385 SK_SMART_QUIRK_4_UNUSED = 0x020000,
1386 SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR = 0x040000,
1387 SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS = 0x080000,
1388 SK_SMART_QUIRK_228_WORKLOADTIMER = 0x100000,
1389 SK_SMART_QUIRK_3_UNUSED = 0x200000
1392 /* %STRINGPOOLSTART% */
1393 static const char *quirk_name[] = {
1396 "9_POWERONHALFMINUTES",
1397 "192_EMERGENCYRETRACTCYCLECT",
1401 "200_WRITEERRORCOUNT",
1402 "201_DETECTEDTACOUNT",
1408 "232_AVAILABLERESERVEDSPACE",
1409 "233_MEDIAWEAROUTINDICATOR",
1410 "225_TOTALLBASWRITTEN",
1412 "226_TIMEWORKLOADMEDIAWEAR",
1413 "227_TIMEWORKLOADHOSTREADS",
1414 "228_WORKLOADTIMER",
1418 /* %STRINGPOOLSTOP% */
1420 typedef struct SkSmartQuirkDatabase {
1422 const char *firmware;
1424 } SkSmartQuirkDatabase;
1426 static const SkSmartQuirkDatabase quirk_database[] = { {
1430 "FUJITSU MHY2120BH|"
1433 "^0085000B$", /* seems to be specific to this firmware */
1434 SK_SMART_QUIRK_9_POWERONMINUTES|
1435 SK_SMART_QUIRK_197_UNKNOWN|
1436 SK_SMART_QUIRK_198_UNKNOWN
1438 "^FUJITSU MHR2040AT$",
1440 SK_SMART_QUIRK_9_POWERONSECONDS|
1441 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1442 SK_SMART_QUIRK_200_WRITEERRORCOUNT
1444 "^FUJITSU MHS20[6432]0AT( .)?$",
1446 SK_SMART_QUIRK_9_POWERONSECONDS|
1447 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1448 SK_SMART_QUIRK_200_WRITEERRORCOUNT|
1449 SK_SMART_QUIRK_201_DETECTEDTACOUNT
1453 "FUJITSU MHG2...ATU?.*|"
1454 "FUJITSU MHH2...ATU?.*|"
1455 "FUJITSU MHJ2...ATU?.*|"
1456 "FUJITSU MHK2...ATU?.*|"
1457 "FUJITSU MHL2300AT|"
1458 "FUJITSU MHM2(20|15|10|06)0AT|"
1459 "FUJITSU MHN2...AT|"
1460 "FUJITSU MHR2020AT|"
1461 "FUJITSU MHT2...(AH|AS|AT|BH)U?.*|"
1462 "FUJITSU MHU2...ATU?.*|"
1463 "FUJITSU MHV2...(AH|AS|AT|BH|BS|BT).*|"
1464 "FUJITSU MP[A-G]3...A[HTEV]U?.*"
1467 SK_SMART_QUIRK_9_POWERONSECONDS
1473 "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]"
1476 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1483 SK_SMART_QUIRK_9_POWERONHALFMINUTES|
1484 SK_SMART_QUIRK_194_10XCELSIUS
1486 "^SAMSUNG SP40A2H$",
1488 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1490 "^SAMSUNG SP80A4H$",
1492 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1494 "^SAMSUNG SP8004H$",
1496 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1501 "Maxtor 2B0(0[468]|1[05]|20)H1|"
1502 "Maxtor 4G(120J6|160J[68])|"
1503 "Maxtor 4D0(20H1|40H2|60H3|80H4)"
1506 SK_SMART_QUIRK_9_POWERONMINUTES|
1507 SK_SMART_QUIRK_194_UNKNOWN
1510 "Maxtor 2F0[234]0[JL]0|"
1511 "Maxtor 8(1280A2|2160A4|2560A4|3840A6|4000A6|5120A8)|"
1512 "Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)|"
1513 "Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)|"
1514 "Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)|"
1515 "Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)|"
1516 "Maxtor 9(2732U8|2390U7|204[09]U6|1707U5|1366U4|1024U3|0845U3|0683U2)|"
1517 "Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)|"
1518 "Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D[34]|90432D2)|"
1519 "Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)|"
1520 "Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)|"
1521 "Maxtor (33073U4|32049U3|31536U2|30768U1|33073H4|32305H3|31536H2|30768H1)|"
1522 "Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)|"
1523 "Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)|"
1524 "Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)|"
1525 "Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?|"
1526 "Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)|"
1527 "Maxtor 9(1023U2|1536U2|2049U3|2305U3|3073U4|4610U6|6147U8)|"
1528 "Maxtor 9(1023H2|1536H2|2049H3|2305H3|3073H4|4098H6|4610H6|6147H8)|"
1529 "Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)|"
1530 "Maxtor (98196H8|96147H6)|"
1531 "Maxtor 4W(100H6|080H6|060H4|040H3|030H2)|"
1532 "Maxtor 6(E0[234]|K04)0L0|"
1533 "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|"
1534 "Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)|"
1535 "Maxtor 7Y250[PM]0|"
1536 "Maxtor [45]A(25|30|32)0[JN]0|"
1537 "Maxtor 7L(25|30)0[SR]0"
1540 SK_SMART_QUIRK_9_POWERONMINUTES
1546 "HITACHI_DK14FA-20B|"
1547 "HITACHI_DK23..-..B?|"
1548 "HITACHI_DK23FA-20J|HTA422020F9AT[JN]0|"
1549 "HE[JN]4230[23]0F9AT00|"
1550 "HTC4260[23]0G5CE00|HTC4260[56]0G8CE00"
1553 SK_SMART_QUIRK_9_POWERONMINUTES|
1554 SK_SMART_QUIRK_193_LOADUNLOAD
1556 "^HTS541010G9SA00$",
1558 SK_SMART_QUIRK_5_UNKNOWN
1561 /*** Apple SSD (?) http://bugs.freedesktop.org/show_bug.cgi?id=24700
1562 https://bugs.launchpad.net/ubuntu/+source/gnome-disk-utility/+bug/438136/comments/4 */
1565 SK_SMART_QUIRK_5_UNKNOWN|
1566 SK_SMART_QUIRK_190_UNKNOWN
1570 "^INTEL SSDSA2CW[0-9]{3}G3$",
1572 SK_SMART_QUIRK_3_UNUSED|
1573 SK_SMART_QUIRK_4_UNUSED|
1574 SK_SMART_QUIRK_225_TOTALLBASWRITTEN|
1575 SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR|
1576 SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS|
1577 SK_SMART_QUIRK_228_WORKLOADTIMER|
1578 SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE|
1579 SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR
1587 static int match(const char*regex, const char *s, SkBool *result) {
1593 if (regcomp(&re, regex, REG_EXTENDED|REG_NOSUB) != 0) {
1598 if ((k = regexec(&re, s, 0, NULL, 0)) != 0) {
1600 if (k != REG_NOMATCH) {
1614 static int lookup_quirks(const char *model, const char *firmware, SkSmartQuirk *quirk) {
1616 const SkSmartQuirkDatabase *db;
1620 for (db = quirk_database; db->model || db->firmware; db++) {
1623 SkBool matching = FALSE;
1625 if ((k = match(db->model, model, &matching)) < 0)
1633 SkBool matching = FALSE;
1635 if ((k = match(db->firmware, firmware, &matching)) < 0)
1649 static const SkSmartAttributeInfo *lookup_attribute(SkDisk *d, uint8_t id) {
1650 const SkIdentifyParsedData *ipd;
1651 SkSmartQuirk quirk = 0;
1653 /* These are the complex ones */
1654 if (sk_disk_identify_parse(d, &ipd) < 0)
1657 if (lookup_quirks(ipd->model, ipd->firmware, &quirk) < 0)
1663 /* %STRINGPOOLSTART% */
1664 if (quirk & SK_SMART_QUIRK_3_UNUSED) {
1665 static const SkSmartAttributeInfo a = {
1666 "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL
1670 /* %STRINGPOOLSTOP% */
1675 /* %STRINGPOOLSTART% */
1676 if (quirk & SK_SMART_QUIRK_4_UNUSED) {
1677 static const SkSmartAttributeInfo a = {
1678 "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL
1682 /* %STRINGPOOLSTOP% */
1687 if (quirk & SK_SMART_QUIRK_5_UNKNOWN)
1693 /* %STRINGPOOLSTART% */
1694 if (quirk & SK_SMART_QUIRK_9_POWERONMINUTES) {
1695 static const SkSmartAttributeInfo a = {
1696 "power-on-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1700 } else if (quirk & SK_SMART_QUIRK_9_POWERONSECONDS) {
1701 static const SkSmartAttributeInfo a = {
1702 "power-on-seconds", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1706 } else if (quirk & SK_SMART_QUIRK_9_POWERONHALFMINUTES) {
1707 static const SkSmartAttributeInfo a = {
1708 "power-on-half-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1711 } else if (quirk & SK_SMART_QUIRK_9_UNKNOWN)
1713 /* %STRINGPOOLSTOP% */
1718 if (quirk & SK_SMART_QUIRK_190_UNKNOWN)
1724 /* %STRINGPOOLSTART% */
1725 if (quirk & SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) {
1726 static const SkSmartAttributeInfo a = {
1727 "emergency-retract-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1731 /* %STRINGPOOLSTOP% */
1736 /* %STRINGPOOLSTART% */
1737 if (quirk & SK_SMART_QUIRK_194_10XCELSIUS) {
1738 static const SkSmartAttributeInfo a = {
1739 "temperature-centi-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature
1742 } else if (quirk & SK_SMART_QUIRK_194_UNKNOWN)
1744 /* %STRINGPOOLSTOP% */
1749 if (quirk & SK_SMART_QUIRK_197_UNKNOWN)
1755 if (quirk & SK_SMART_QUIRK_198_UNKNOWN)
1761 /* %STRINGPOOLSTART% */
1762 if (quirk & SK_SMART_QUIRK_200_WRITEERRORCOUNT) {
1763 static const SkSmartAttributeInfo a = {
1764 "write-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1768 /* %STRINGPOOLSTOP% */
1773 /* %STRINGPOOLSTART% */
1774 if (quirk & SK_SMART_QUIRK_201_DETECTEDTACOUNT) {
1775 static const SkSmartAttributeInfo a = {
1776 "detected-ta-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1780 /* %STRINGPOOLSTOP% */
1785 /* %STRINGPOOLSTART% */
1786 if (quirk & SK_SMART_QUIRK_225_TOTALLBASWRITTEN) {
1787 static const SkSmartAttributeInfo a = {
1788 "total-lbas-written", SK_SMART_ATTRIBUTE_UNIT_MB, NULL
1792 /* %STRINGPOOLSTOP% */
1797 /* %STRINGPOOLSTART% */
1798 if (quirk & SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR) {
1799 static const SkSmartAttributeInfo a = {
1800 "timed-workload-media-wear", SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT, NULL
1804 /* %STRINGPOOLSTOP% */
1809 /* %STRINGPOOLSTART% */
1810 if (quirk & SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS) {
1811 static const SkSmartAttributeInfo a = {
1812 "timed-workload-host-reads", SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT, NULL
1816 /* %STRINGPOOLSTOP% */
1821 /* %STRINGPOOLSTART% */
1822 if (quirk & SK_SMART_QUIRK_228_WORKLOADTIMER) {
1823 static const SkSmartAttributeInfo a = {
1824 "workload-timer", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, NULL
1828 /* %STRINGPOOLSTOP% */
1833 /* %STRINGPOOLSTART% */
1834 if (quirk & SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE) {
1835 static const SkSmartAttributeInfo a = {
1836 "available-reserved-space", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL
1840 /* %STRINGPOOLSTOP% */
1844 /* %STRINGPOOLSTART% */
1845 if (quirk & SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR) {
1846 static const SkSmartAttributeInfo a = {
1847 "media-wearout-indicator", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL
1851 /* %STRINGPOOLSTOP% */
1857 /* These are the simple cases */
1858 if (attribute_info[id].name)
1859 return &attribute_info[id];
1864 int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **spd) {
1866 if (!d->smart_data_valid) {
1871 switch (d->smart_data[362]) {
1874 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER;
1879 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS;
1883 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS;
1888 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED;
1893 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED;
1898 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL;
1902 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN;
1906 d->smart_parsed_data.self_test_execution_percent_remaining = 10*(d->smart_data[363] & 0xF);
1907 d->smart_parsed_data.self_test_execution_status = (d->smart_data[363] >> 4) & 0xF;
1909 d->smart_parsed_data.total_offline_data_collection_seconds = (uint16_t) d->smart_data[364] | ((uint16_t) d->smart_data[365] << 8);
1911 d->smart_parsed_data.conveyance_test_available = disk_smart_is_conveyance_test_available(d);
1912 d->smart_parsed_data.short_and_extended_test_available = disk_smart_is_short_and_extended_test_available(d);
1913 d->smart_parsed_data.start_test_available = disk_smart_is_start_test_available(d);
1914 d->smart_parsed_data.abort_test_available = disk_smart_is_abort_test_available(d);
1916 d->smart_parsed_data.short_test_polling_minutes = d->smart_data[372];
1917 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]);
1918 d->smart_parsed_data.conveyance_test_polling_minutes = d->smart_data[374];
1920 *spd = &d->smart_parsed_data;
1925 static void find_threshold(SkDisk *d, SkSmartAttributeParsedData *a) {
1929 if (!d->smart_thresholds_valid)
1932 for (n = 0, p = d->smart_thresholds+2; n < 30; n++, p+=12)
1939 a->threshold = p[1];
1940 a->threshold_valid = p[1] != 0xFE;
1942 a->good_now_valid = FALSE;
1944 a->good_in_the_past_valid = FALSE;
1945 a->good_in_the_past = TRUE;
1947 /* Always-Fail and Always-Passing thresholds are not relevant
1948 * for our assessment. */
1949 if (p[1] >= 1 && p[1] <= 0xFD) {
1951 if (a->worst_value_valid) {
1952 a->good_in_the_past = a->good_in_the_past && (a->worst_value > a->threshold);
1953 a->good_in_the_past_valid = TRUE;
1956 if (a->current_value_valid) {
1957 a->good_now = a->good_now && (a->current_value > a->threshold);
1958 a->good_now_valid = TRUE;
1963 (a->good_now_valid && !a->good_now) ||
1964 (a->good_in_the_past_valid && !a->good_in_the_past);
1969 a->threshold_valid = FALSE;
1970 a->good_now_valid = FALSE;
1971 a->good_in_the_past_valid = FALSE;
1975 int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, void* userdata) {
1979 if (!d->smart_data_valid) {
1984 for (n = 0, p = d->smart_data + 2; n < 30; n++, p+=12) {
1985 SkSmartAttributeParsedData a;
1986 const SkSmartAttributeInfo *i;
1992 memset(&a, 0, sizeof(a));
1994 a.current_value = p[3];
1995 a.current_value_valid = p[3] >= 1 && p[3] <= 0xFD;
1996 a.worst_value = p[4];
1997 a.worst_value_valid = p[4] >= 1 && p[4] <= 0xFD;
1999 a.flags = ((uint16_t) p[2] << 8) | p[1];
2000 a.prefailure = !!(p[1] & 1);
2001 a.online = !!(p[1] & 2);
2003 memcpy(a.raw, p+5, 6);
2005 if ((i = lookup_attribute(d, p[0]))) {
2006 a.name = _P(i->name);
2007 a.pretty_unit = i->unit;
2009 if (asprintf(&an, "attribute-%u", a.id) < 0) {
2015 a.pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
2020 find_threshold(d, &a);
2025 cb(d, &a, userdata);
2032 static const char *yes_no(SkBool b) {
2033 return b ? "yes" : "no";
2036 const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit) {
2038 /* %STRINGPOOLSTART% */
2039 const char * const map[] = {
2040 [SK_SMART_ATTRIBUTE_UNIT_UNKNOWN] = NULL,
2041 [SK_SMART_ATTRIBUTE_UNIT_NONE] = "",
2042 [SK_SMART_ATTRIBUTE_UNIT_MSECONDS] = "ms",
2043 [SK_SMART_ATTRIBUTE_UNIT_SECTORS] = "sectors",
2044 [SK_SMART_ATTRIBUTE_UNIT_MKELVIN] = "mK",
2045 [SK_SMART_ATTRIBUTE_UNIT_PERCENT] = "%",
2046 [SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT] = "%",
2047 [SK_SMART_ATTRIBUTE_UNIT_MB] = "MB"
2049 /* %STRINGPOOLSTOP% */
2051 if (unit >= _SK_SMART_ATTRIBUTE_UNIT_MAX)
2054 return _P(map[unit]);
2057 struct attr_helper {
2062 static void temperature_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2064 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MKELVIN)
2067 if (!strcmp(a->name, "temperature-centi-celsius") ||
2068 !strcmp(a->name, "temperature-celsius") ||
2069 !strcmp(a->name, "temperature-celsius-2") ||
2070 !strcmp(a->name, "airflow-temperature-celsius")) {
2072 if (!ah->found || a->pretty_value > *ah->value)
2073 *ah->value = a->pretty_value;
2079 int sk_disk_smart_get_temperature(SkDisk *d, uint64_t *kelvin) {
2080 struct attr_helper ah;
2088 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) temperature_cb, &ah) < 0)
2099 static void power_on_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2101 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MSECONDS)
2104 if (!strcmp(a->name, "power-on-minutes") ||
2105 !strcmp(a->name, "power-on-seconds") ||
2106 !strcmp(a->name, "power-on-seconds-2") ||
2107 !strcmp(a->name, "power-on-half-minutes") ||
2108 !strcmp(a->name, "power-on-hours")) {
2110 if (!ah->found || a->pretty_value > *ah->value)
2111 *ah->value = a->pretty_value;
2117 int sk_disk_smart_get_power_on(SkDisk *d, uint64_t *mseconds) {
2118 struct attr_helper ah;
2124 ah.value = mseconds;
2126 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_on_cb, &ah) < 0)
2137 static void power_cycle_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2139 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_NONE)
2142 if (!strcmp(a->name, "power-cycle-count")) {
2144 if (!ah->found || a->pretty_value > *ah->value)
2145 *ah->value = a->pretty_value;
2151 int sk_disk_smart_get_power_cycle(SkDisk *d, uint64_t *count) {
2152 struct attr_helper ah;
2160 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_cycle_cb, &ah) < 0)
2171 static void fill_cache_cb(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) {
2173 if (a->prefailure) {
2174 if (a->good_now_valid && !a->good_now)
2175 d->bad_attribute_now = TRUE;
2177 if (a->good_in_the_past_valid && !a->good_in_the_past)
2178 d->bad_attribute_in_the_past = TRUE;
2181 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
2184 if (!strcmp(a->name, "reallocated-sector-count")) {
2185 if (a->pretty_value > d->reallocated_sector_count)
2186 d->reallocated_sector_count = a->pretty_value;
2187 d->reallocated_sector_count_found = TRUE;
2190 if (!strcmp(a->name, "current-pending-sector")) {
2191 if (a->pretty_value > d->current_pending_sector)
2192 d->current_pending_sector = a->pretty_value;
2193 d->current_pending_sector_found = TRUE;
2197 static int fill_cache(SkDisk *d) {
2198 if (d->attribute_cache_valid)
2201 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) fill_cache_cb, NULL) >= 0) {
2202 d->attribute_cache_valid = TRUE;
2208 int sk_disk_smart_get_bad(SkDisk *d, uint64_t *sectors) {
2212 if (fill_cache (d) < 0)
2215 if (!d->reallocated_sector_count_found && !d->current_pending_sector_found) {
2220 if (d->reallocated_sector_count_found && d->current_pending_sector_found)
2221 *sectors = d->reallocated_sector_count + d->current_pending_sector;
2222 else if (d->reallocated_sector_count_found)
2223 *sectors = d->reallocated_sector_count;
2225 *sectors = d->current_pending_sector;
2230 const char* sk_smart_overall_to_string(SkSmartOverall overall) {
2232 /* %STRINGPOOLSTART% */
2233 const char * const map[] = {
2234 [SK_SMART_OVERALL_GOOD] = "GOOD",
2235 [SK_SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST] = "BAD_ATTRIBUTE_IN_THE_PAST",
2236 [SK_SMART_OVERALL_BAD_SECTOR] = "BAD_SECTOR",
2237 [SK_SMART_OVERALL_BAD_ATTRIBUTE_NOW] = "BAD_ATTRIBUTE_NOW",
2238 [SK_SMART_OVERALL_BAD_SECTOR_MANY] = "BAD_SECTOR_MANY",
2239 [SK_SMART_OVERALL_BAD_STATUS] = "BAD_STATUS",
2241 /* %STRINGPOOLSTOP% */
2243 if (overall >= _SK_SMART_OVERALL_MAX)
2246 return _P(map[overall]);
2249 static uint64_t u64log2(uint64_t n) {
2264 int sk_disk_smart_get_overall(SkDisk *d, SkSmartOverall *overall) {
2266 uint64_t sectors, sector_threshold;
2271 /* First, check SMART self-assesment */
2272 if (sk_disk_smart_status(d, &good) < 0)
2276 *overall = SK_SMART_OVERALL_BAD_STATUS;
2280 /* Second, check if the number of bad sectors is greater than
2281 * a certain threshold */
2282 if (sk_disk_smart_get_bad(d, §ors) < 0) {
2283 if (errno != ENOENT)
2288 /* We use log2(n_sectors) as a threshold here. We had to pick
2289 * something, and this makes a bit of sense, or doesn't it? */
2290 sector_threshold = u64log2(d->size/512);
2292 if (sectors >= sector_threshold) {
2293 *overall = SK_SMART_OVERALL_BAD_SECTOR_MANY;
2298 /* Third, check if any of the SMART attributes is bad */
2299 if (fill_cache (d) < 0)
2302 if (d->bad_attribute_now) {
2303 *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE_NOW;
2307 /* Fourth, check if there are any bad sectors at all */
2309 *overall = SK_SMART_OVERALL_BAD_SECTOR;
2313 /* Fifth, check if any of the SMART attributes ever was bad */
2314 if (d->bad_attribute_in_the_past) {
2315 *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST;
2319 /* Sixth, there's really nothing to complain about, so give it a pass */
2320 *overall = SK_SMART_OVERALL_GOOD;
2324 static char* print_name(char *s, size_t len, uint8_t id, const char *k) {
2329 snprintf(s, len, "%u", id);
2336 static char *print_value(char *s, size_t len, uint64_t pretty_value, SkSmartAttributeUnit pretty_unit) {
2338 switch (pretty_unit) {
2339 case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
2341 if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU)
2342 snprintf(s, len, "%0.1f years", ((double) pretty_value)/(1000.0*60*60*24*365));
2343 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU)
2344 snprintf(s, len, "%0.1f months", ((double) pretty_value)/(1000.0*60*60*24*30));
2345 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU)
2346 snprintf(s, len, "%0.1f days", ((double) pretty_value)/(1000.0*60*60*24));
2347 else if (pretty_value >= 1000LLU*60LLU*60LLU)
2348 snprintf(s, len, "%0.1f h", ((double) pretty_value)/(1000.0*60*60));
2349 else if (pretty_value >= 1000LLU*60LLU)
2350 snprintf(s, len, "%0.1f min", ((double) pretty_value)/(1000.0*60));
2351 else if (pretty_value >= 1000LLU)
2352 snprintf(s, len, "%0.1f s", ((double) pretty_value)/(1000.0));
2354 snprintf(s, len, "%llu ms", (unsigned long long) pretty_value);
2358 case SK_SMART_ATTRIBUTE_UNIT_MKELVIN:
2359 snprintf(s, len, "%0.1f C", ((double) pretty_value - 273150) / 1000);
2362 case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
2363 snprintf(s, len, "%llu sectors", (unsigned long long) pretty_value);
2366 case SK_SMART_ATTRIBUTE_UNIT_PERCENT:
2367 snprintf(s, len, "%llu%%", (unsigned long long) pretty_value);
2370 case SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT:
2371 snprintf(s, len, "%0.3f%%", (double) pretty_value);
2374 case SK_SMART_ATTRIBUTE_UNIT_MB:
2375 if (pretty_value >= 1000000LLU)
2376 snprintf(s, len, "%0.3f TB", (double) pretty_value / 1000000LLU);
2377 else if (pretty_value >= 1000LLU)
2378 snprintf(s, len, "%0.3f GB", (double) pretty_value / 1000LLU);
2380 snprintf(s, len, "%llu MB", (unsigned long long) pretty_value);
2383 case SK_SMART_ATTRIBUTE_UNIT_NONE:
2384 snprintf(s, len, "%llu", (unsigned long long) pretty_value);
2387 case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
2388 snprintf(s, len, "n/a");
2391 case _SK_SMART_ATTRIBUTE_UNIT_MAX:
2400 #define HIGHLIGHT "\x1B[1m"
2401 #define ENDHIGHLIGHT "\x1B[0m"
2403 static void disk_dump_attributes(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) {
2406 char tt[32], tw[32], tc[32];
2409 snprintf(tt, sizeof(tt), "%3u", a->threshold);
2410 tt[sizeof(tt)-1] = 0;
2411 snprintf(tw, sizeof(tw), "%3u", a->worst_value);
2412 tw[sizeof(tw)-1] = 0;
2413 snprintf(tc, sizeof(tc), "%3u", a->current_value);
2414 tc[sizeof(tc)-1] = 0;
2416 highlight = a->warn && isatty(1);
2419 fprintf(stderr, HIGHLIGHT);
2421 printf("%3u %-27s %-3s %-3s %-3s %-11s 0x%02x%02x%02x%02x%02x%02x %-7s %-7s %-4s %-4s\n",
2423 print_name(name, sizeof(name), a->id, a->name),
2424 a->current_value_valid ? tc : "n/a",
2425 a->worst_value_valid ? tw : "n/a",
2426 a->threshold_valid ? tt : "n/a",
2427 print_value(pretty, sizeof(pretty), a->pretty_value, a->pretty_unit),
2428 a->raw[0], a->raw[1], a->raw[2], a->raw[3], a->raw[4], a->raw[5],
2429 a->prefailure ? "prefail" : "old-age",
2430 a->online ? "online" : "offline",
2431 a->good_now_valid ? yes_no(a->good_now) : "n/a",
2432 a->good_in_the_past_valid ? yes_no(a->good_in_the_past) : "n/a");
2435 fprintf(stderr, ENDHIGHLIGHT);
2438 int sk_disk_dump(SkDisk *d) {
2440 SkBool awake = FALSE;
2445 printf("Device: %s%s%s\n"
2447 d->name ? disk_type_to_prefix_string(d->type) : "",
2449 d->name ? d->name : "n/a",
2450 disk_type_to_human_string(d->type));
2452 ret = sk_disk_get_size(d, &size);
2454 printf("Size: %lu MiB\n", (unsigned long) (d->size/1024/1024));
2456 printf("Size: %s\n", strerror(errno));
2458 if (d->identify_valid) {
2459 const SkIdentifyParsedData *ipd;
2460 SkSmartQuirk quirk = 0;
2463 if ((ret = sk_disk_identify_parse(d, &ipd)) < 0)
2466 printf("Model: [%s]\n"
2469 "SMART Available: %s\n",
2473 yes_no(disk_smart_is_available(d)));
2475 if ((ret = lookup_quirks(ipd->model, ipd->firmware, &quirk)))
2480 for (i = 0; quirk_name[i]; i++)
2482 printf(" %s", _P(quirk_name[i]));
2487 ret = sk_disk_check_sleep_mode(d, &awake);
2488 printf("Awake: %s\n",
2489 ret >= 0 ? yes_no(awake) : strerror(errno));
2491 if (disk_smart_is_available(d)) {
2492 SkSmartOverall overall;
2493 const SkSmartParsedData *spd;
2496 uint64_t value, power_on;
2498 ret = sk_disk_smart_status(d, &good);
2499 printf("%sSMART Disk Health Good: %s%s\n",
2500 ret >= 0 && !good ? HIGHLIGHT : "",
2501 ret >= 0 ? yes_no(good) : strerror(errno),
2502 ret >= 0 && !good ? ENDHIGHLIGHT : "");
2503 if ((ret = sk_disk_smart_read_data(d)) < 0)
2506 if ((ret = sk_disk_smart_parse(d, &spd)) < 0)
2509 printf("Off-line Data Collection Status: [%s]\n"
2510 "Total Time To Complete Off-Line Data Collection: %u s\n"
2511 "Self-Test Execution Status: [%s]\n"
2512 "Percent Self-Test Remaining: %u%%\n"
2513 "Conveyance Self-Test Available: %s\n"
2514 "Short/Extended Self-Test Available: %s\n"
2515 "Start Self-Test Available: %s\n"
2516 "Abort Self-Test Available: %s\n"
2517 "Short Self-Test Polling Time: %u min\n"
2518 "Extended Self-Test Polling Time: %u min\n"
2519 "Conveyance Self-Test Polling Time: %u min\n",
2520 sk_smart_offline_data_collection_status_to_string(spd->offline_data_collection_status),
2521 spd->total_offline_data_collection_seconds,
2522 sk_smart_self_test_execution_status_to_string(spd->self_test_execution_status),
2523 spd->self_test_execution_percent_remaining,
2524 yes_no(spd->conveyance_test_available),
2525 yes_no(spd->short_and_extended_test_available),
2526 yes_no(spd->start_test_available),
2527 yes_no(spd->abort_test_available),
2528 spd->short_test_polling_minutes,
2529 spd->extended_test_polling_minutes,
2530 spd->conveyance_test_polling_minutes);
2532 if (sk_disk_smart_get_bad(d, &value) < 0)
2533 printf("Bad Sectors: %s\n", strerror(errno));
2535 printf("%sBad Sectors: %s%s\n",
2536 value > 0 ? HIGHLIGHT : "",
2537 print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_SECTORS),
2538 value > 0 ? ENDHIGHLIGHT : "");
2540 if (sk_disk_smart_get_power_on(d, &power_on) < 0) {
2541 printf("Powered On: %s\n", strerror(errno));
2544 printf("Powered On: %s\n", print_value(pretty, sizeof(pretty), power_on, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2546 if (sk_disk_smart_get_power_cycle(d, &value) < 0)
2547 printf("Power Cycles: %s\n", strerror(errno));
2549 printf("Power Cycles: %llu\n", (unsigned long long) value);
2551 if (value > 0 && power_on > 0)
2552 printf("Average Powered On Per Power Cycle: %s\n", print_value(pretty, sizeof(pretty), power_on/value, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2555 if (sk_disk_smart_get_temperature(d, &value) < 0)
2556 printf("Temperature: %s\n", strerror(errno));
2558 printf("Temperature: %s\n", print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_MKELVIN));
2560 printf("Attribute Parsing Verification: %s\n",
2561 d->attribute_verification_bad ? "Bad" : "Good");
2563 if (sk_disk_smart_get_overall(d, &overall) < 0)
2564 printf("Overall Status: %s\n", strerror(errno));
2566 printf("%sOverall Status: %s%s\n",
2567 overall != SK_SMART_OVERALL_GOOD ? HIGHLIGHT : "",
2568 sk_smart_overall_to_string(overall),
2569 overall != SK_SMART_OVERALL_GOOD ? ENDHIGHLIGHT : "");
2571 printf("%3s %-27s %5s %5s %5s %-11s %-14s %-7s %-7s %-4s %-4s\n",
2584 if ((ret = sk_disk_smart_parse_attributes(d, disk_dump_attributes, NULL)) < 0)
2587 printf("ATA SMART not supported.\n");
2592 int sk_disk_get_size(SkDisk *d, uint64_t *bytes) {
2596 if (d->size == (uint64_t) -1) {
2605 static int disk_find_type(SkDisk *d, dev_t devnum) {
2607 struct udev_device *dev = NULL, *usb;
2613 if (!(udev = udev_new())) {
2618 if (!(dev = udev_device_new_from_devnum(udev, 'b', devnum))) {
2623 if ((a = udev_device_get_property_value(dev, "ID_ATA_SMART_ACCESS"))) {
2626 for (u = 0; u < _SK_DISK_TYPE_MAX; u++) {
2629 if (!(t = disk_type_to_prefix_string(u)))
2632 if (!strcmp(a, t)) {
2639 d->type = SK_DISK_TYPE_NONE;
2644 if ((usb = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"))) {
2645 const char *product, *vendor;
2648 if (!(product = udev_device_get_sysattr_value(usb, "idProduct")) ||
2649 sscanf(product, "%04x", &pid) != 1) {
2654 if (!(vendor = udev_device_get_sysattr_value(usb, "idVendor")) ||
2655 sscanf(vendor, "%04x", &vid) != 1) {
2660 if ((vid == 0x0928 && pid == 0x0000))
2661 /* This Oxford Semiconductor bridge seems to
2662 * choke on SAT commands. Let's explicitly
2663 * black list it here.
2665 * http://bugs.freedesktop.org/show_bug.cgi?id=24951 */
2666 d->type = SK_DISK_TYPE_NONE;
2667 else if ((vid == 0x152d && pid == 0x2329) ||
2668 (vid == 0x152d && pid == 0x2338) ||
2669 (vid == 0x152d && pid == 0x2339))
2670 /* Some JMicron bridges seem to choke on SMART
2671 * commands, so let's explicitly black list
2674 * https://bugzilla.redhat.com/show_bug.cgi?id=515881
2676 * At least some of the JMicron bridges with
2677 * these vids/pids choke on the jmicron access
2678 * mode. To make sure we don't break things
2679 * for people we now disable this by
2681 d->type = SK_DISK_TYPE_NONE;
2682 else if ((vid == 0x152d && pid == 0x2336))
2683 /* This JMicron bridge seems to always work
2684 * with SMART commands send with the jmicron
2686 d->type = SK_DISK_TYPE_JMICRON;
2687 else if ((vid == 0x0c0b && pid == 0xb159) ||
2688 (vid == 0x04fc && pid == 0x0c25) ||
2689 (vid == 0x04fc && pid == 0x0c15))
2690 d->type = SK_DISK_TYPE_SUNPLUS;
2692 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_12;
2694 } else if (udev_device_get_parent_with_subsystem_devtype(dev, "ide", NULL))
2695 d->type = SK_DISK_TYPE_LINUX_IDE;
2696 else if (udev_device_get_parent_with_subsystem_devtype(dev, "scsi", NULL))
2697 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_16;
2699 d->type = SK_DISK_TYPE_AUTO;
2705 udev_device_unref(dev);
2713 static int init_smart(SkDisk *d) {
2714 /* We don't do the SMART initialization right-away, since some
2715 * drivers spin up when we do that */
2719 if (d->smart_initialized)
2722 d->smart_initialized = TRUE;
2724 /* Check if driver can do SMART, and enable if necessary */
2725 if (!disk_smart_is_available(d))
2728 if (!disk_smart_is_enabled(d)) {
2729 if ((ret = disk_smart_enable(d, TRUE)) < 0)
2732 if ((ret = disk_identify_device(d)) < 0)
2735 if (!disk_smart_is_enabled(d)) {
2742 disk_smart_read_thresholds(d);
2749 int sk_disk_open(const char *name, SkDisk **_d) {
2756 if (!(d = calloc(1, sizeof(SkDisk)))) {
2762 d->size = (uint64_t) -1;
2765 d->type = SK_DISK_TYPE_BLOB;
2769 d->type = SK_DISK_TYPE_AUTO;
2771 if (!(dn = disk_type_from_string(name, &d->type)))
2774 if (!(d->name = strdup(dn))) {
2779 if ((d->fd = open(d->name,
2780 O_RDONLY|O_NOCTTY|O_NONBLOCK
2790 if ((ret = fstat(d->fd, &st)) < 0)
2793 if (!S_ISBLK(st.st_mode)) {
2799 /* So, it's a block device. Let's make sure the ioctls work */
2800 if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0)
2803 if (d->size <= 0 || d->size == (uint64_t) -1) {
2809 /* OK, it's a real block device with a size. Now let's find the suitable API */
2810 if (d->type == SK_DISK_TYPE_AUTO)
2811 if ((ret = disk_find_type(d, st.st_rdev)) < 0)
2814 if (d->type == SK_DISK_TYPE_AUTO) {
2815 /* We have no clue, so let's autotest for a working API */
2816 for (d->type = 0; d->type < _SK_DISK_TYPE_TEST_MAX; d->type++)
2817 if (disk_identify_device(d) >= 0)
2819 if (d->type >= _SK_DISK_TYPE_TEST_MAX)
2820 d->type = SK_DISK_TYPE_NONE;
2822 disk_identify_device(d);
2837 void sk_disk_free(SkDisk *d) {
2848 int sk_disk_get_blob(SkDisk *d, const void **blob, size_t *rsize) {
2850 SkBool good, have_good = FALSE;
2858 (d->identify_valid ? 8 + sizeof(d->identify) : 0) +
2859 (d->smart_data_valid ? 8 + sizeof(d->smart_data) : 0) +
2860 (d->smart_thresholds_valid ? 8 + sizeof(d->smart_thresholds) : 0);
2862 if (sk_disk_smart_status(d, &good) >= 0) {
2873 if (!(d->blob = malloc(size))) {
2880 /* These memory accesses are only OK as long as all our
2881 * objects are sensibly aligned, which they are... */
2883 if (d->identify_valid) {
2884 p[0] = SK_BLOB_TAG_IDENTIFY;
2885 p[1] = htonl(sizeof(d->identify));
2888 memcpy(p, d->identify, sizeof(d->identify));
2889 p = (uint32_t*) ((uint8_t*) p + sizeof(d->identify));
2893 p[0] = SK_BLOB_TAG_SMART_STATUS;
2895 p[2] = htonl(!!good);
2899 if (d->smart_data_valid) {
2900 p[0] = SK_BLOB_TAG_SMART_DATA;
2901 p[1] = htonl(sizeof(d->smart_data));
2904 memcpy(p, d->smart_data, sizeof(d->smart_data));
2905 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_data));
2908 if (d->smart_thresholds_valid) {
2909 p[0] = SK_BLOB_TAG_SMART_THRESHOLDS;
2910 p[1] = htonl(sizeof(d->smart_thresholds));
2913 memcpy(p, d->smart_thresholds, sizeof(d->smart_thresholds));
2914 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_thresholds));
2917 assert((size_t) ((uint8_t*) p - (uint8_t*) d->blob) == size);
2925 int sk_disk_set_blob(SkDisk *d, const void *blob, size_t size) {
2928 SkBool idv = FALSE, sdv = FALSE, stv = FALSE, bssv = FALSE;
2933 if (d->type != SK_DISK_TYPE_BLOB) {
2943 /* First run, verify if everything makes sense */
2947 uint32_t tag, tsize;
2955 memcpy(&tsize, p+1, 4);
2959 if (left < ntohl(tsize)) {
2966 case SK_BLOB_TAG_IDENTIFY:
2967 if (ntohl(tsize) != sizeof(d->identify) || idv) {
2974 case SK_BLOB_TAG_SMART_STATUS:
2975 if (ntohl(tsize) != 4 || bssv) {
2982 case SK_BLOB_TAG_SMART_DATA:
2983 if (ntohl(tsize) != sizeof(d->smart_data) || sdv) {
2990 case SK_BLOB_TAG_SMART_THRESHOLDS:
2991 if (ntohl(tsize) != sizeof(d->smart_thresholds) || stv) {
2999 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
3000 left -= ntohl(tsize);
3008 d->identify_valid = idv;
3009 d->smart_data_valid = sdv;
3010 d->smart_thresholds_valid = stv;
3011 d->blob_smart_status_valid = bssv;
3013 /* Second run, actually copy things in */
3017 uint32_t tag, tsize;
3021 memcpy(&tsize, p+1, 4);
3025 assert(left >= ntohl(tsize));
3029 case SK_BLOB_TAG_IDENTIFY:
3030 assert(ntohl(tsize) == sizeof(d->identify));
3031 memcpy(d->identify, p, sizeof(d->identify));
3034 case SK_BLOB_TAG_SMART_STATUS: {
3036 assert(ntohl(tsize) == 4);
3038 d->blob_smart_status = !!ok;
3042 case SK_BLOB_TAG_SMART_DATA:
3043 assert(ntohl(tsize) == sizeof(d->smart_data));
3044 memcpy(d->smart_data, p, sizeof(d->smart_data));
3047 case SK_BLOB_TAG_SMART_THRESHOLDS:
3048 assert(ntohl(tsize) == sizeof(d->smart_thresholds));
3049 memcpy(d->smart_thresholds, p, sizeof(d->smart_thresholds));
3053 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
3054 left -= ntohl(tsize);