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 [175] = { "program-fail-count-chip", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1319 [176] = { "erase-fail-count-chip", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1320 [177] = { "wear-leveling-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1321 [178] = { "used-reserved-blocks-chip", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1322 [179] = { "used-reserved-blocks-total", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1323 [180] = { "unused-reserved-blocks", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1324 [181] = { "program-fail-count-total", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1325 [182] = { "erase-fail-count-total", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1326 [183] = { "runtime-bad-block-total", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1327 [184] = { "end-to-end-error", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1328 [187] = { "reported-uncorrect", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1329 [188] = { "command-timeout", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1330 [189] = { "high-fly-writes", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1331 [190] = { "airflow-temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1332 [191] = { "g-sense-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1333 [192] = { "power-off-retract-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1334 [193] = { "load-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1335 [194] = { "temperature-celsius-2", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1336 [195] = { "hardware-ecc-recovered", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1337 [196] = { "reallocated-event-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1338 [197] = { "current-pending-sector", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1339 [198] = { "offline-uncorrectable", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1340 [199] = { "udma-crc-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1341 [200] = { "multi-zone-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1342 [201] = { "soft-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1343 [202] = { "ta-increase-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1344 [203] = { "run-out-cancel", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1345 [204] = { "shock-count-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1346 [205] = { "shock-rate-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1347 [206] = { "flying-height", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1348 [207] = { "spin-high-current", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1349 [208] = { "spin-buzz", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1350 [209] = { "offline-seek-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1351 [220] = { "disk-shift", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1352 [221] = { "g-sense-error-rate-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1353 [222] = { "loaded-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1354 [223] = { "load-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1355 [224] = { "load-friction", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1356 [225] = { "load-cycle-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1357 [226] = { "load-in-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_short_time },
1358 [227] = { "torq-amp-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1359 [228] = { "power-off-retract-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1360 [230] = { "head-amplitude", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1361 [231] = { "temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1363 /* http://www.adtron.com/pdf/SMART_for_XceedLite_SATA_RevA.pdf */
1364 [232] = { "endurance-remaining", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL },
1365 [233] = { "power-on-seconds-2", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1366 [234] = { "uncorrectable-ecc-count", SK_SMART_ATTRIBUTE_UNIT_SECTORS, NULL },
1367 [235] = { "good-block-rate", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1369 [240] = { "head-flying-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1370 [241] = { "total-lbas-written", SK_SMART_ATTRIBUTE_UNIT_MB, NULL },
1371 [242] = { "total-lbas-read", SK_SMART_ATTRIBUTE_UNIT_MB, NULL },
1372 [250] = { "read-error-retry-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }
1374 /* %STRINGPOOLSTOP% */
1376 typedef enum SkSmartQuirk {
1377 SK_SMART_QUIRK_9_POWERONMINUTES = 0x000001,
1378 SK_SMART_QUIRK_9_POWERONSECONDS = 0x000002,
1379 SK_SMART_QUIRK_9_POWERONHALFMINUTES = 0x000004,
1380 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT = 0x000008,
1381 SK_SMART_QUIRK_193_LOADUNLOAD = 0x000010,
1382 SK_SMART_QUIRK_194_10XCELSIUS = 0x000020,
1383 SK_SMART_QUIRK_194_UNKNOWN = 0x000040,
1384 SK_SMART_QUIRK_200_WRITEERRORCOUNT = 0x000080,
1385 SK_SMART_QUIRK_201_DETECTEDTACOUNT = 0x000100,
1386 SK_SMART_QUIRK_5_UNKNOWN = 0x000200,
1387 SK_SMART_QUIRK_9_UNKNOWN = 0x000400,
1388 SK_SMART_QUIRK_197_UNKNOWN = 0x000800,
1389 SK_SMART_QUIRK_198_UNKNOWN = 0x001000,
1390 SK_SMART_QUIRK_190_UNKNOWN = 0x002000,
1391 SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE = 0x004000,
1392 SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR = 0x008000,
1393 SK_SMART_QUIRK_225_TOTALLBASWRITTEN = 0x010000,
1394 SK_SMART_QUIRK_4_UNUSED = 0x020000,
1395 SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR = 0x040000,
1396 SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS = 0x080000,
1397 SK_SMART_QUIRK_228_WORKLOADTIMER = 0x100000,
1398 SK_SMART_QUIRK_3_UNUSED = 0x200000
1401 /* %STRINGPOOLSTART% */
1402 static const char *quirk_name[] = {
1405 "9_POWERONHALFMINUTES",
1406 "192_EMERGENCYRETRACTCYCLECT",
1410 "200_WRITEERRORCOUNT",
1411 "201_DETECTEDTACOUNT",
1417 "232_AVAILABLERESERVEDSPACE",
1418 "233_MEDIAWEAROUTINDICATOR",
1419 "225_TOTALLBASWRITTEN",
1421 "226_TIMEWORKLOADMEDIAWEAR",
1422 "227_TIMEWORKLOADHOSTREADS",
1423 "228_WORKLOADTIMER",
1427 /* %STRINGPOOLSTOP% */
1429 typedef struct SkSmartQuirkDatabase {
1431 const char *firmware;
1433 } SkSmartQuirkDatabase;
1435 static const SkSmartQuirkDatabase quirk_database[] = { {
1439 "FUJITSU MHY2120BH|"
1442 "^0085000B$", /* seems to be specific to this firmware */
1443 SK_SMART_QUIRK_9_POWERONMINUTES|
1444 SK_SMART_QUIRK_197_UNKNOWN|
1445 SK_SMART_QUIRK_198_UNKNOWN
1447 "^FUJITSU MHR2040AT$",
1449 SK_SMART_QUIRK_9_POWERONSECONDS|
1450 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1451 SK_SMART_QUIRK_200_WRITEERRORCOUNT
1453 "^FUJITSU MHS20[6432]0AT( .)?$",
1455 SK_SMART_QUIRK_9_POWERONSECONDS|
1456 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1457 SK_SMART_QUIRK_200_WRITEERRORCOUNT|
1458 SK_SMART_QUIRK_201_DETECTEDTACOUNT
1462 "FUJITSU MHG2...ATU?.*|"
1463 "FUJITSU MHH2...ATU?.*|"
1464 "FUJITSU MHJ2...ATU?.*|"
1465 "FUJITSU MHK2...ATU?.*|"
1466 "FUJITSU MHL2300AT|"
1467 "FUJITSU MHM2(20|15|10|06)0AT|"
1468 "FUJITSU MHN2...AT|"
1469 "FUJITSU MHR2020AT|"
1470 "FUJITSU MHT2...(AH|AS|AT|BH)U?.*|"
1471 "FUJITSU MHU2...ATU?.*|"
1472 "FUJITSU MHV2...(AH|AS|AT|BH|BS|BT).*|"
1473 "FUJITSU MP[A-G]3...A[HTEV]U?.*"
1476 SK_SMART_QUIRK_9_POWERONSECONDS
1482 "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]"
1485 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1492 SK_SMART_QUIRK_9_POWERONHALFMINUTES|
1493 SK_SMART_QUIRK_194_10XCELSIUS
1495 "^SAMSUNG SP40A2H$",
1497 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1499 "^SAMSUNG SP80A4H$",
1501 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1503 "^SAMSUNG SP8004H$",
1505 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1510 "Maxtor 2B0(0[468]|1[05]|20)H1|"
1511 "Maxtor 4G(120J6|160J[68])|"
1512 "Maxtor 4D0(20H1|40H2|60H3|80H4)"
1515 SK_SMART_QUIRK_9_POWERONMINUTES|
1516 SK_SMART_QUIRK_194_UNKNOWN
1519 "Maxtor 2F0[234]0[JL]0|"
1520 "Maxtor 8(1280A2|2160A4|2560A4|3840A6|4000A6|5120A8)|"
1521 "Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)|"
1522 "Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)|"
1523 "Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)|"
1524 "Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)|"
1525 "Maxtor 9(2732U8|2390U7|204[09]U6|1707U5|1366U4|1024U3|0845U3|0683U2)|"
1526 "Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)|"
1527 "Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D[34]|90432D2)|"
1528 "Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)|"
1529 "Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)|"
1530 "Maxtor (33073U4|32049U3|31536U2|30768U1|33073H4|32305H3|31536H2|30768H1)|"
1531 "Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)|"
1532 "Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)|"
1533 "Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)|"
1534 "Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?|"
1535 "Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)|"
1536 "Maxtor 9(1023U2|1536U2|2049U3|2305U3|3073U4|4610U6|6147U8)|"
1537 "Maxtor 9(1023H2|1536H2|2049H3|2305H3|3073H4|4098H6|4610H6|6147H8)|"
1538 "Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)|"
1539 "Maxtor (98196H8|96147H6)|"
1540 "Maxtor 4W(100H6|080H6|060H4|040H3|030H2)|"
1541 "Maxtor 6(E0[234]|K04)0L0|"
1542 "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|"
1543 "Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)|"
1544 "Maxtor 7Y250[PM]0|"
1545 "Maxtor [45]A(25|30|32)0[JN]0|"
1546 "Maxtor 7L(25|30)0[SR]0"
1549 SK_SMART_QUIRK_9_POWERONMINUTES
1555 "HITACHI_DK14FA-20B|"
1556 "HITACHI_DK23..-..B?|"
1557 "HITACHI_DK23FA-20J|HTA422020F9AT[JN]0|"
1558 "HE[JN]4230[23]0F9AT00|"
1559 "HTC4260[23]0G5CE00|HTC4260[56]0G8CE00"
1562 SK_SMART_QUIRK_9_POWERONMINUTES|
1563 SK_SMART_QUIRK_193_LOADUNLOAD
1565 "^HTS541010G9SA00$",
1567 SK_SMART_QUIRK_5_UNKNOWN
1570 /*** Apple SSD (?) http://bugs.freedesktop.org/show_bug.cgi?id=24700
1571 https://bugs.launchpad.net/ubuntu/+source/gnome-disk-utility/+bug/438136/comments/4 */
1574 SK_SMART_QUIRK_5_UNKNOWN|
1575 SK_SMART_QUIRK_190_UNKNOWN
1579 "^INTEL SSDSA2(CT|BT|CW|BW)[0-9]{3}G3.*$", /* 320 Series */
1581 SK_SMART_QUIRK_3_UNUSED|
1582 SK_SMART_QUIRK_4_UNUSED|
1583 SK_SMART_QUIRK_225_TOTALLBASWRITTEN|
1584 SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR|
1585 SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS|
1586 SK_SMART_QUIRK_228_WORKLOADTIMER|
1587 SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE|
1588 SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR
1596 static int match(const char*regex, const char *s, SkBool *result) {
1602 if (regcomp(&re, regex, REG_EXTENDED|REG_NOSUB) != 0) {
1607 if ((k = regexec(&re, s, 0, NULL, 0)) != 0) {
1609 if (k != REG_NOMATCH) {
1623 static int lookup_quirks(const char *model, const char *firmware, SkSmartQuirk *quirk) {
1625 const SkSmartQuirkDatabase *db;
1629 for (db = quirk_database; db->model || db->firmware; db++) {
1632 SkBool matching = FALSE;
1634 if ((k = match(db->model, model, &matching)) < 0)
1642 SkBool matching = FALSE;
1644 if ((k = match(db->firmware, firmware, &matching)) < 0)
1658 static const SkSmartAttributeInfo *lookup_attribute(SkDisk *d, uint8_t id) {
1659 const SkIdentifyParsedData *ipd;
1660 SkSmartQuirk quirk = 0;
1662 /* These are the complex ones */
1663 if (sk_disk_identify_parse(d, &ipd) < 0)
1666 if (lookup_quirks(ipd->model, ipd->firmware, &quirk) < 0)
1672 /* %STRINGPOOLSTART% */
1673 if (quirk & SK_SMART_QUIRK_3_UNUSED) {
1674 static const SkSmartAttributeInfo a = {
1675 "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL
1679 /* %STRINGPOOLSTOP% */
1684 /* %STRINGPOOLSTART% */
1685 if (quirk & SK_SMART_QUIRK_4_UNUSED) {
1686 static const SkSmartAttributeInfo a = {
1687 "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL
1691 /* %STRINGPOOLSTOP% */
1696 if (quirk & SK_SMART_QUIRK_5_UNKNOWN)
1702 /* %STRINGPOOLSTART% */
1703 if (quirk & SK_SMART_QUIRK_9_POWERONMINUTES) {
1704 static const SkSmartAttributeInfo a = {
1705 "power-on-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1709 } else if (quirk & SK_SMART_QUIRK_9_POWERONSECONDS) {
1710 static const SkSmartAttributeInfo a = {
1711 "power-on-seconds", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1715 } else if (quirk & SK_SMART_QUIRK_9_POWERONHALFMINUTES) {
1716 static const SkSmartAttributeInfo a = {
1717 "power-on-half-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1720 } else if (quirk & SK_SMART_QUIRK_9_UNKNOWN)
1722 /* %STRINGPOOLSTOP% */
1727 if (quirk & SK_SMART_QUIRK_190_UNKNOWN)
1733 /* %STRINGPOOLSTART% */
1734 if (quirk & SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) {
1735 static const SkSmartAttributeInfo a = {
1736 "emergency-retract-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1740 /* %STRINGPOOLSTOP% */
1745 /* %STRINGPOOLSTART% */
1746 if (quirk & SK_SMART_QUIRK_194_10XCELSIUS) {
1747 static const SkSmartAttributeInfo a = {
1748 "temperature-centi-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature
1751 } else if (quirk & SK_SMART_QUIRK_194_UNKNOWN)
1753 /* %STRINGPOOLSTOP% */
1758 if (quirk & SK_SMART_QUIRK_197_UNKNOWN)
1764 if (quirk & SK_SMART_QUIRK_198_UNKNOWN)
1770 /* %STRINGPOOLSTART% */
1771 if (quirk & SK_SMART_QUIRK_200_WRITEERRORCOUNT) {
1772 static const SkSmartAttributeInfo a = {
1773 "write-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1777 /* %STRINGPOOLSTOP% */
1782 /* %STRINGPOOLSTART% */
1783 if (quirk & SK_SMART_QUIRK_201_DETECTEDTACOUNT) {
1784 static const SkSmartAttributeInfo a = {
1785 "detected-ta-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1789 /* %STRINGPOOLSTOP% */
1794 /* %STRINGPOOLSTART% */
1795 if (quirk & SK_SMART_QUIRK_225_TOTALLBASWRITTEN) {
1796 static const SkSmartAttributeInfo a = {
1797 "total-lbas-written", SK_SMART_ATTRIBUTE_UNIT_MB, NULL
1801 /* %STRINGPOOLSTOP% */
1806 /* %STRINGPOOLSTART% */
1807 if (quirk & SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR) {
1808 static const SkSmartAttributeInfo a = {
1809 "timed-workload-media-wear", SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT, NULL
1813 /* %STRINGPOOLSTOP% */
1818 /* %STRINGPOOLSTART% */
1819 if (quirk & SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS) {
1820 static const SkSmartAttributeInfo a = {
1821 "timed-workload-host-reads", SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT, NULL
1825 /* %STRINGPOOLSTOP% */
1830 /* %STRINGPOOLSTART% */
1831 if (quirk & SK_SMART_QUIRK_228_WORKLOADTIMER) {
1832 static const SkSmartAttributeInfo a = {
1833 "workload-timer", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, NULL
1837 /* %STRINGPOOLSTOP% */
1842 /* %STRINGPOOLSTART% */
1843 if (quirk & SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE) {
1844 static const SkSmartAttributeInfo a = {
1845 "available-reserved-space", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL
1849 /* %STRINGPOOLSTOP% */
1853 /* %STRINGPOOLSTART% */
1854 if (quirk & SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR) {
1855 static const SkSmartAttributeInfo a = {
1856 "media-wearout-indicator", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL
1860 /* %STRINGPOOLSTOP% */
1866 /* These are the simple cases */
1867 if (attribute_info[id].name)
1868 return &attribute_info[id];
1873 int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **spd) {
1875 if (!d->smart_data_valid) {
1880 switch (d->smart_data[362]) {
1883 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER;
1888 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS;
1892 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS;
1897 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED;
1902 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED;
1907 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL;
1911 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN;
1915 d->smart_parsed_data.self_test_execution_percent_remaining = 10*(d->smart_data[363] & 0xF);
1916 d->smart_parsed_data.self_test_execution_status = (d->smart_data[363] >> 4) & 0xF;
1918 d->smart_parsed_data.total_offline_data_collection_seconds = (uint16_t) d->smart_data[364] | ((uint16_t) d->smart_data[365] << 8);
1920 d->smart_parsed_data.conveyance_test_available = disk_smart_is_conveyance_test_available(d);
1921 d->smart_parsed_data.short_and_extended_test_available = disk_smart_is_short_and_extended_test_available(d);
1922 d->smart_parsed_data.start_test_available = disk_smart_is_start_test_available(d);
1923 d->smart_parsed_data.abort_test_available = disk_smart_is_abort_test_available(d);
1925 d->smart_parsed_data.short_test_polling_minutes = d->smart_data[372];
1926 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]);
1927 d->smart_parsed_data.conveyance_test_polling_minutes = d->smart_data[374];
1929 *spd = &d->smart_parsed_data;
1934 static void find_threshold(SkDisk *d, SkSmartAttributeParsedData *a) {
1938 if (!d->smart_thresholds_valid)
1941 for (n = 0, p = d->smart_thresholds+2; n < 30; n++, p+=12)
1948 a->threshold = p[1];
1949 a->threshold_valid = p[1] != 0xFE;
1951 a->good_now_valid = FALSE;
1953 a->good_in_the_past_valid = FALSE;
1954 a->good_in_the_past = TRUE;
1956 /* Always-Fail and Always-Passing thresholds are not relevant
1957 * for our assessment. */
1958 if (p[1] >= 1 && p[1] <= 0xFD) {
1960 if (a->worst_value_valid) {
1961 a->good_in_the_past = a->good_in_the_past && (a->worst_value > a->threshold);
1962 a->good_in_the_past_valid = TRUE;
1965 if (a->current_value_valid) {
1966 a->good_now = a->good_now && (a->current_value > a->threshold);
1967 a->good_now_valid = TRUE;
1972 (a->good_now_valid && !a->good_now) ||
1973 (a->good_in_the_past_valid && !a->good_in_the_past);
1978 a->threshold_valid = FALSE;
1979 a->good_now_valid = FALSE;
1980 a->good_in_the_past_valid = FALSE;
1984 int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, void* userdata) {
1988 if (!d->smart_data_valid) {
1993 for (n = 0, p = d->smart_data + 2; n < 30; n++, p+=12) {
1994 SkSmartAttributeParsedData a;
1995 const SkSmartAttributeInfo *i;
2001 memset(&a, 0, sizeof(a));
2003 a.current_value = p[3];
2004 a.current_value_valid = p[3] >= 1 && p[3] <= 0xFD;
2005 a.worst_value = p[4];
2006 a.worst_value_valid = p[4] >= 1 && p[4] <= 0xFD;
2008 a.flags = ((uint16_t) p[2] << 8) | p[1];
2009 a.prefailure = !!(p[1] & 1);
2010 a.online = !!(p[1] & 2);
2012 memcpy(a.raw, p+5, 6);
2014 if ((i = lookup_attribute(d, p[0]))) {
2015 a.name = _P(i->name);
2016 a.pretty_unit = i->unit;
2018 if (asprintf(&an, "attribute-%u", a.id) < 0) {
2024 a.pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
2029 find_threshold(d, &a);
2034 cb(d, &a, userdata);
2041 static const char *yes_no(SkBool b) {
2042 return b ? "yes" : "no";
2045 const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit) {
2047 /* %STRINGPOOLSTART% */
2048 const char * const map[] = {
2049 [SK_SMART_ATTRIBUTE_UNIT_UNKNOWN] = NULL,
2050 [SK_SMART_ATTRIBUTE_UNIT_NONE] = "",
2051 [SK_SMART_ATTRIBUTE_UNIT_MSECONDS] = "ms",
2052 [SK_SMART_ATTRIBUTE_UNIT_SECTORS] = "sectors",
2053 [SK_SMART_ATTRIBUTE_UNIT_MKELVIN] = "mK",
2054 [SK_SMART_ATTRIBUTE_UNIT_PERCENT] = "%",
2055 [SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT] = "%",
2056 [SK_SMART_ATTRIBUTE_UNIT_MB] = "MB"
2058 /* %STRINGPOOLSTOP% */
2060 if (unit >= _SK_SMART_ATTRIBUTE_UNIT_MAX)
2063 return _P(map[unit]);
2066 struct attr_helper {
2071 static void temperature_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2073 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MKELVIN)
2076 if (!strcmp(a->name, "temperature-centi-celsius") ||
2077 !strcmp(a->name, "temperature-celsius") ||
2078 !strcmp(a->name, "temperature-celsius-2") ||
2079 !strcmp(a->name, "airflow-temperature-celsius")) {
2081 if (!ah->found || a->pretty_value > *ah->value)
2082 *ah->value = a->pretty_value;
2088 int sk_disk_smart_get_temperature(SkDisk *d, uint64_t *kelvin) {
2089 struct attr_helper ah;
2097 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) temperature_cb, &ah) < 0)
2108 static void power_on_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2110 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MSECONDS)
2113 if (!strcmp(a->name, "power-on-minutes") ||
2114 !strcmp(a->name, "power-on-seconds") ||
2115 !strcmp(a->name, "power-on-seconds-2") ||
2116 !strcmp(a->name, "power-on-half-minutes") ||
2117 !strcmp(a->name, "power-on-hours")) {
2119 if (!ah->found || a->pretty_value > *ah->value)
2120 *ah->value = a->pretty_value;
2126 int sk_disk_smart_get_power_on(SkDisk *d, uint64_t *mseconds) {
2127 struct attr_helper ah;
2133 ah.value = mseconds;
2135 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_on_cb, &ah) < 0)
2146 static void power_cycle_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2148 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_NONE)
2151 if (!strcmp(a->name, "power-cycle-count")) {
2153 if (!ah->found || a->pretty_value > *ah->value)
2154 *ah->value = a->pretty_value;
2160 int sk_disk_smart_get_power_cycle(SkDisk *d, uint64_t *count) {
2161 struct attr_helper ah;
2169 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_cycle_cb, &ah) < 0)
2180 static void fill_cache_cb(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) {
2182 if (a->prefailure) {
2183 if (a->good_now_valid && !a->good_now)
2184 d->bad_attribute_now = TRUE;
2186 if (a->good_in_the_past_valid && !a->good_in_the_past)
2187 d->bad_attribute_in_the_past = TRUE;
2190 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
2193 if (!strcmp(a->name, "reallocated-sector-count")) {
2194 if (a->pretty_value > d->reallocated_sector_count)
2195 d->reallocated_sector_count = a->pretty_value;
2196 d->reallocated_sector_count_found = TRUE;
2199 if (!strcmp(a->name, "current-pending-sector")) {
2200 if (a->pretty_value > d->current_pending_sector)
2201 d->current_pending_sector = a->pretty_value;
2202 d->current_pending_sector_found = TRUE;
2206 static int fill_cache(SkDisk *d) {
2207 if (d->attribute_cache_valid)
2210 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) fill_cache_cb, NULL) >= 0) {
2211 d->attribute_cache_valid = TRUE;
2217 int sk_disk_smart_get_bad(SkDisk *d, uint64_t *sectors) {
2221 if (fill_cache (d) < 0)
2224 if (!d->reallocated_sector_count_found && !d->current_pending_sector_found) {
2229 if (d->reallocated_sector_count_found && d->current_pending_sector_found)
2230 *sectors = d->reallocated_sector_count + d->current_pending_sector;
2231 else if (d->reallocated_sector_count_found)
2232 *sectors = d->reallocated_sector_count;
2234 *sectors = d->current_pending_sector;
2239 const char* sk_smart_overall_to_string(SkSmartOverall overall) {
2241 /* %STRINGPOOLSTART% */
2242 const char * const map[] = {
2243 [SK_SMART_OVERALL_GOOD] = "GOOD",
2244 [SK_SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST] = "BAD_ATTRIBUTE_IN_THE_PAST",
2245 [SK_SMART_OVERALL_BAD_SECTOR] = "BAD_SECTOR",
2246 [SK_SMART_OVERALL_BAD_ATTRIBUTE_NOW] = "BAD_ATTRIBUTE_NOW",
2247 [SK_SMART_OVERALL_BAD_SECTOR_MANY] = "BAD_SECTOR_MANY",
2248 [SK_SMART_OVERALL_BAD_STATUS] = "BAD_STATUS",
2250 /* %STRINGPOOLSTOP% */
2252 if (overall >= _SK_SMART_OVERALL_MAX)
2255 return _P(map[overall]);
2258 static uint64_t u64log2(uint64_t n) {
2273 int sk_disk_smart_get_overall(SkDisk *d, SkSmartOverall *overall) {
2275 uint64_t sectors, sector_threshold;
2280 /* First, check SMART self-assesment */
2281 if (sk_disk_smart_status(d, &good) < 0)
2285 *overall = SK_SMART_OVERALL_BAD_STATUS;
2289 /* Second, check if the number of bad sectors is greater than
2290 * a certain threshold */
2291 if (sk_disk_smart_get_bad(d, §ors) < 0) {
2292 if (errno != ENOENT)
2297 /* We use log2(n_sectors)*1024 as a threshold here. We
2298 * had to pick something, and this makes a bit of
2299 * sense, or doesn't it? */
2300 sector_threshold = u64log2(d->size/512) * 1024;
2302 if (sectors >= sector_threshold) {
2303 *overall = SK_SMART_OVERALL_BAD_SECTOR_MANY;
2308 /* Third, check if any of the SMART attributes is bad */
2309 if (fill_cache (d) < 0)
2312 if (d->bad_attribute_now) {
2313 *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE_NOW;
2317 /* Fourth, check if there are any bad sectors at all */
2319 *overall = SK_SMART_OVERALL_BAD_SECTOR;
2323 /* Fifth, check if any of the SMART attributes ever was bad */
2324 if (d->bad_attribute_in_the_past) {
2325 *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST;
2329 /* Sixth, there's really nothing to complain about, so give it a pass */
2330 *overall = SK_SMART_OVERALL_GOOD;
2334 static char* print_name(char *s, size_t len, uint8_t id, const char *k) {
2339 snprintf(s, len, "%u", id);
2346 static char *print_value(char *s, size_t len, uint64_t pretty_value, SkSmartAttributeUnit pretty_unit) {
2348 switch (pretty_unit) {
2349 case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
2351 if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU)
2352 snprintf(s, len, "%0.1f years", ((double) pretty_value)/(1000.0*60*60*24*365));
2353 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU)
2354 snprintf(s, len, "%0.1f months", ((double) pretty_value)/(1000.0*60*60*24*30));
2355 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU)
2356 snprintf(s, len, "%0.1f days", ((double) pretty_value)/(1000.0*60*60*24));
2357 else if (pretty_value >= 1000LLU*60LLU*60LLU)
2358 snprintf(s, len, "%0.1f h", ((double) pretty_value)/(1000.0*60*60));
2359 else if (pretty_value >= 1000LLU*60LLU)
2360 snprintf(s, len, "%0.1f min", ((double) pretty_value)/(1000.0*60));
2361 else if (pretty_value >= 1000LLU)
2362 snprintf(s, len, "%0.1f s", ((double) pretty_value)/(1000.0));
2364 snprintf(s, len, "%llu ms", (unsigned long long) pretty_value);
2368 case SK_SMART_ATTRIBUTE_UNIT_MKELVIN:
2369 snprintf(s, len, "%0.1f C", ((double) pretty_value - 273150) / 1000);
2372 case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
2373 snprintf(s, len, "%llu sectors", (unsigned long long) pretty_value);
2376 case SK_SMART_ATTRIBUTE_UNIT_PERCENT:
2377 snprintf(s, len, "%llu%%", (unsigned long long) pretty_value);
2380 case SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT:
2381 snprintf(s, len, "%0.3f%%", (double) pretty_value);
2384 case SK_SMART_ATTRIBUTE_UNIT_MB:
2385 if (pretty_value >= 1000000LLU)
2386 snprintf(s, len, "%0.3f TB", (double) pretty_value / 1000000LLU);
2387 else if (pretty_value >= 1000LLU)
2388 snprintf(s, len, "%0.3f GB", (double) pretty_value / 1000LLU);
2390 snprintf(s, len, "%llu MB", (unsigned long long) pretty_value);
2393 case SK_SMART_ATTRIBUTE_UNIT_NONE:
2394 snprintf(s, len, "%llu", (unsigned long long) pretty_value);
2397 case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
2398 snprintf(s, len, "n/a");
2401 case _SK_SMART_ATTRIBUTE_UNIT_MAX:
2410 #define HIGHLIGHT "\x1B[1m"
2411 #define ENDHIGHLIGHT "\x1B[0m"
2413 static void disk_dump_attributes(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) {
2416 char tt[32], tw[32], tc[32];
2419 snprintf(tt, sizeof(tt), "%3u", a->threshold);
2420 tt[sizeof(tt)-1] = 0;
2421 snprintf(tw, sizeof(tw), "%3u", a->worst_value);
2422 tw[sizeof(tw)-1] = 0;
2423 snprintf(tc, sizeof(tc), "%3u", a->current_value);
2424 tc[sizeof(tc)-1] = 0;
2426 highlight = a->warn && isatty(1);
2429 fprintf(stderr, HIGHLIGHT);
2431 printf("%3u %-27s %-3s %-3s %-3s %-11s 0x%02x%02x%02x%02x%02x%02x %-7s %-7s %-4s %-4s\n",
2433 print_name(name, sizeof(name), a->id, a->name),
2434 a->current_value_valid ? tc : "n/a",
2435 a->worst_value_valid ? tw : "n/a",
2436 a->threshold_valid ? tt : "n/a",
2437 print_value(pretty, sizeof(pretty), a->pretty_value, a->pretty_unit),
2438 a->raw[0], a->raw[1], a->raw[2], a->raw[3], a->raw[4], a->raw[5],
2439 a->prefailure ? "prefail" : "old-age",
2440 a->online ? "online" : "offline",
2441 a->good_now_valid ? yes_no(a->good_now) : "n/a",
2442 a->good_in_the_past_valid ? yes_no(a->good_in_the_past) : "n/a");
2445 fprintf(stderr, ENDHIGHLIGHT);
2448 int sk_disk_dump(SkDisk *d) {
2450 SkBool awake = FALSE;
2455 printf("Device: %s%s%s\n"
2457 d->name ? disk_type_to_prefix_string(d->type) : "",
2459 d->name ? d->name : "n/a",
2460 disk_type_to_human_string(d->type));
2462 ret = sk_disk_get_size(d, &size);
2464 printf("Size: %lu MiB\n", (unsigned long) (d->size/1024/1024));
2466 printf("Size: %s\n", strerror(errno));
2468 if (d->identify_valid) {
2469 const SkIdentifyParsedData *ipd;
2470 SkSmartQuirk quirk = 0;
2473 if ((ret = sk_disk_identify_parse(d, &ipd)) < 0)
2476 printf("Model: [%s]\n"
2479 "SMART Available: %s\n",
2483 yes_no(disk_smart_is_available(d)));
2485 if ((ret = lookup_quirks(ipd->model, ipd->firmware, &quirk)))
2490 for (i = 0; quirk_name[i]; i++)
2492 printf(" %s", _P(quirk_name[i]));
2497 ret = sk_disk_check_sleep_mode(d, &awake);
2498 printf("Awake: %s\n",
2499 ret >= 0 ? yes_no(awake) : strerror(errno));
2501 if (disk_smart_is_available(d)) {
2502 SkSmartOverall overall;
2503 const SkSmartParsedData *spd;
2506 uint64_t value, power_on;
2508 ret = sk_disk_smart_status(d, &good);
2509 printf("%sSMART Disk Health Good: %s%s\n",
2510 ret >= 0 && !good ? HIGHLIGHT : "",
2511 ret >= 0 ? yes_no(good) : strerror(errno),
2512 ret >= 0 && !good ? ENDHIGHLIGHT : "");
2513 if ((ret = sk_disk_smart_read_data(d)) < 0)
2516 if ((ret = sk_disk_smart_parse(d, &spd)) < 0)
2519 printf("Off-line Data Collection Status: [%s]\n"
2520 "Total Time To Complete Off-Line Data Collection: %u s\n"
2521 "Self-Test Execution Status: [%s]\n"
2522 "Percent Self-Test Remaining: %u%%\n"
2523 "Conveyance Self-Test Available: %s\n"
2524 "Short/Extended Self-Test Available: %s\n"
2525 "Start Self-Test Available: %s\n"
2526 "Abort Self-Test Available: %s\n"
2527 "Short Self-Test Polling Time: %u min\n"
2528 "Extended Self-Test Polling Time: %u min\n"
2529 "Conveyance Self-Test Polling Time: %u min\n",
2530 sk_smart_offline_data_collection_status_to_string(spd->offline_data_collection_status),
2531 spd->total_offline_data_collection_seconds,
2532 sk_smart_self_test_execution_status_to_string(spd->self_test_execution_status),
2533 spd->self_test_execution_percent_remaining,
2534 yes_no(spd->conveyance_test_available),
2535 yes_no(spd->short_and_extended_test_available),
2536 yes_no(spd->start_test_available),
2537 yes_no(spd->abort_test_available),
2538 spd->short_test_polling_minutes,
2539 spd->extended_test_polling_minutes,
2540 spd->conveyance_test_polling_minutes);
2542 if (sk_disk_smart_get_bad(d, &value) < 0)
2543 printf("Bad Sectors: %s\n", strerror(errno));
2545 printf("%sBad Sectors: %s%s\n",
2546 value > 0 ? HIGHLIGHT : "",
2547 print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_SECTORS),
2548 value > 0 ? ENDHIGHLIGHT : "");
2550 if (sk_disk_smart_get_power_on(d, &power_on) < 0) {
2551 printf("Powered On: %s\n", strerror(errno));
2554 printf("Powered On: %s\n", print_value(pretty, sizeof(pretty), power_on, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2556 if (sk_disk_smart_get_power_cycle(d, &value) < 0)
2557 printf("Power Cycles: %s\n", strerror(errno));
2559 printf("Power Cycles: %llu\n", (unsigned long long) value);
2561 if (value > 0 && power_on > 0)
2562 printf("Average Powered On Per Power Cycle: %s\n", print_value(pretty, sizeof(pretty), power_on/value, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2565 if (sk_disk_smart_get_temperature(d, &value) < 0)
2566 printf("Temperature: %s\n", strerror(errno));
2568 printf("Temperature: %s\n", print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_MKELVIN));
2570 printf("Attribute Parsing Verification: %s\n",
2571 d->attribute_verification_bad ? "Bad" : "Good");
2573 if (sk_disk_smart_get_overall(d, &overall) < 0)
2574 printf("Overall Status: %s\n", strerror(errno));
2576 printf("%sOverall Status: %s%s\n",
2577 overall != SK_SMART_OVERALL_GOOD ? HIGHLIGHT : "",
2578 sk_smart_overall_to_string(overall),
2579 overall != SK_SMART_OVERALL_GOOD ? ENDHIGHLIGHT : "");
2581 printf("%3s %-27s %5s %5s %5s %-11s %-14s %-7s %-7s %-4s %-4s\n",
2594 if ((ret = sk_disk_smart_parse_attributes(d, disk_dump_attributes, NULL)) < 0)
2597 printf("ATA SMART not supported.\n");
2602 int sk_disk_get_size(SkDisk *d, uint64_t *bytes) {
2606 if (d->size == (uint64_t) -1) {
2615 static int disk_find_type(SkDisk *d, dev_t devnum) {
2617 struct udev_device *dev = NULL, *usb;
2623 if (!(udev = udev_new())) {
2628 if (!(dev = udev_device_new_from_devnum(udev, 'b', devnum))) {
2633 if ((a = udev_device_get_property_value(dev, "ID_ATA_SMART_ACCESS"))) {
2636 for (u = 0; u < _SK_DISK_TYPE_MAX; u++) {
2639 if (!(t = disk_type_to_prefix_string(u)))
2642 if (!strcmp(a, t)) {
2649 d->type = SK_DISK_TYPE_NONE;
2654 if ((usb = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"))) {
2655 const char *product, *vendor;
2658 if (!(product = udev_device_get_sysattr_value(usb, "idProduct")) ||
2659 sscanf(product, "%04x", &pid) != 1) {
2664 if (!(vendor = udev_device_get_sysattr_value(usb, "idVendor")) ||
2665 sscanf(vendor, "%04x", &vid) != 1) {
2670 if ((vid == 0x0928 && pid == 0x0000))
2671 /* This Oxford Semiconductor bridge seems to
2672 * choke on SAT commands. Let's explicitly
2673 * black list it here.
2675 * http://bugs.freedesktop.org/show_bug.cgi?id=24951 */
2676 d->type = SK_DISK_TYPE_NONE;
2677 else if ((vid == 0x152d && pid == 0x2329) ||
2678 (vid == 0x152d && pid == 0x2338) ||
2679 (vid == 0x152d && pid == 0x2339))
2680 /* Some JMicron bridges seem to choke on SMART
2681 * commands, so let's explicitly black list
2684 * https://bugzilla.redhat.com/show_bug.cgi?id=515881
2686 * At least some of the JMicron bridges with
2687 * these vids/pids choke on the jmicron access
2688 * mode. To make sure we don't break things
2689 * for people we now disable this by
2691 d->type = SK_DISK_TYPE_NONE;
2692 else if ((vid == 0x152d && pid == 0x2336))
2693 /* This JMicron bridge seems to always work
2694 * with SMART commands send with the jmicron
2696 d->type = SK_DISK_TYPE_JMICRON;
2697 else if ((vid == 0x0c0b && pid == 0xb159) ||
2698 (vid == 0x04fc && pid == 0x0c25) ||
2699 (vid == 0x04fc && pid == 0x0c15))
2700 d->type = SK_DISK_TYPE_SUNPLUS;
2702 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_12;
2704 } else if (udev_device_get_parent_with_subsystem_devtype(dev, "ide", NULL))
2705 d->type = SK_DISK_TYPE_LINUX_IDE;
2706 else if (udev_device_get_parent_with_subsystem_devtype(dev, "scsi", NULL))
2707 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_16;
2709 d->type = SK_DISK_TYPE_AUTO;
2715 udev_device_unref(dev);
2723 static int init_smart(SkDisk *d) {
2724 /* We don't do the SMART initialization right-away, since some
2725 * drivers spin up when we do that */
2729 if (d->smart_initialized)
2732 d->smart_initialized = TRUE;
2734 /* Check if driver can do SMART, and enable if necessary */
2735 if (!disk_smart_is_available(d))
2738 if (!disk_smart_is_enabled(d)) {
2739 if ((ret = disk_smart_enable(d, TRUE)) < 0)
2742 if ((ret = disk_identify_device(d)) < 0)
2745 if (!disk_smart_is_enabled(d)) {
2752 disk_smart_read_thresholds(d);
2759 int sk_disk_open(const char *name, SkDisk **_d) {
2766 if (!(d = calloc(1, sizeof(SkDisk)))) {
2772 d->size = (uint64_t) -1;
2775 d->type = SK_DISK_TYPE_BLOB;
2779 d->type = SK_DISK_TYPE_AUTO;
2781 if (!(dn = disk_type_from_string(name, &d->type)))
2784 if (!(d->name = strdup(dn))) {
2789 if ((d->fd = open(d->name,
2790 O_RDONLY|O_NOCTTY|O_NONBLOCK
2800 if ((ret = fstat(d->fd, &st)) < 0)
2803 if (!S_ISBLK(st.st_mode)) {
2809 /* So, it's a block device. Let's make sure the ioctls work */
2810 if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0)
2813 if (d->size <= 0 || d->size == (uint64_t) -1) {
2819 /* OK, it's a real block device with a size. Now let's find the suitable API */
2820 if (d->type == SK_DISK_TYPE_AUTO)
2821 if ((ret = disk_find_type(d, st.st_rdev)) < 0)
2824 if (d->type == SK_DISK_TYPE_AUTO) {
2825 /* We have no clue, so let's autotest for a working API */
2826 for (d->type = 0; d->type < _SK_DISK_TYPE_TEST_MAX; d->type++)
2827 if (disk_identify_device(d) >= 0)
2829 if (d->type >= _SK_DISK_TYPE_TEST_MAX)
2830 d->type = SK_DISK_TYPE_NONE;
2832 disk_identify_device(d);
2847 void sk_disk_free(SkDisk *d) {
2858 int sk_disk_get_blob(SkDisk *d, const void **blob, size_t *rsize) {
2860 SkBool good, have_good = FALSE;
2868 (d->identify_valid ? 8 + sizeof(d->identify) : 0) +
2869 (d->smart_data_valid ? 8 + sizeof(d->smart_data) : 0) +
2870 (d->smart_thresholds_valid ? 8 + sizeof(d->smart_thresholds) : 0);
2872 if (sk_disk_smart_status(d, &good) >= 0) {
2883 if (!(d->blob = malloc(size))) {
2890 /* These memory accesses are only OK as long as all our
2891 * objects are sensibly aligned, which they are... */
2893 if (d->identify_valid) {
2894 p[0] = SK_BLOB_TAG_IDENTIFY;
2895 p[1] = htonl(sizeof(d->identify));
2898 memcpy(p, d->identify, sizeof(d->identify));
2899 p = (uint32_t*) ((uint8_t*) p + sizeof(d->identify));
2903 p[0] = SK_BLOB_TAG_SMART_STATUS;
2905 p[2] = htonl(!!good);
2909 if (d->smart_data_valid) {
2910 p[0] = SK_BLOB_TAG_SMART_DATA;
2911 p[1] = htonl(sizeof(d->smart_data));
2914 memcpy(p, d->smart_data, sizeof(d->smart_data));
2915 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_data));
2918 if (d->smart_thresholds_valid) {
2919 p[0] = SK_BLOB_TAG_SMART_THRESHOLDS;
2920 p[1] = htonl(sizeof(d->smart_thresholds));
2923 memcpy(p, d->smart_thresholds, sizeof(d->smart_thresholds));
2924 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_thresholds));
2927 assert((size_t) ((uint8_t*) p - (uint8_t*) d->blob) == size);
2935 int sk_disk_set_blob(SkDisk *d, const void *blob, size_t size) {
2938 SkBool idv = FALSE, sdv = FALSE, stv = FALSE, bssv = FALSE;
2943 if (d->type != SK_DISK_TYPE_BLOB) {
2953 /* First run, verify if everything makes sense */
2957 uint32_t tag, tsize;
2965 memcpy(&tsize, p+1, 4);
2969 if (left < ntohl(tsize)) {
2976 case SK_BLOB_TAG_IDENTIFY:
2977 if (ntohl(tsize) != sizeof(d->identify) || idv) {
2984 case SK_BLOB_TAG_SMART_STATUS:
2985 if (ntohl(tsize) != 4 || bssv) {
2992 case SK_BLOB_TAG_SMART_DATA:
2993 if (ntohl(tsize) != sizeof(d->smart_data) || sdv) {
3000 case SK_BLOB_TAG_SMART_THRESHOLDS:
3001 if (ntohl(tsize) != sizeof(d->smart_thresholds) || stv) {
3009 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
3010 left -= ntohl(tsize);
3018 d->identify_valid = idv;
3019 d->smart_data_valid = sdv;
3020 d->smart_thresholds_valid = stv;
3021 d->blob_smart_status_valid = bssv;
3023 /* Second run, actually copy things in */
3027 uint32_t tag, tsize;
3031 memcpy(&tsize, p+1, 4);
3035 assert(left >= ntohl(tsize));
3039 case SK_BLOB_TAG_IDENTIFY:
3040 assert(ntohl(tsize) == sizeof(d->identify));
3041 memcpy(d->identify, p, sizeof(d->identify));
3044 case SK_BLOB_TAG_SMART_STATUS: {
3046 assert(ntohl(tsize) == 4);
3048 d->blob_smart_status = !!ok;
3052 case SK_BLOB_TAG_SMART_DATA:
3053 assert(ntohl(tsize) == sizeof(d->smart_data));
3054 memcpy(d->smart_data, p, sizeof(d->smart_data));
3057 case SK_BLOB_TAG_SMART_THRESHOLDS:
3058 assert(ntohl(tsize) == sizeof(d->smart_thresholds));
3059 memcpy(d->smart_thresholds, p, sizeof(d->smart_thresholds));
3063 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
3064 left -= ntohl(tsize);