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;
129 typedef enum SkAtaCommand {
130 SK_ATA_COMMAND_IDENTIFY_DEVICE = 0xEC,
131 SK_ATA_COMMAND_IDENTIFY_PACKET_DEVICE = 0xA1,
132 SK_ATA_COMMAND_SMART = 0xB0,
133 SK_ATA_COMMAND_CHECK_POWER_MODE = 0xE5
136 /* ATA SMART subcommands (ATA8 7.52.1) */
137 typedef enum SkSmartCommand {
138 SK_SMART_COMMAND_READ_DATA = 0xD0,
139 SK_SMART_COMMAND_READ_THRESHOLDS = 0xD1,
140 SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE = 0xD4,
141 SK_SMART_COMMAND_ENABLE_OPERATIONS = 0xD8,
142 SK_SMART_COMMAND_DISABLE_OPERATIONS = 0xD9,
143 SK_SMART_COMMAND_RETURN_STATUS = 0xDA
146 /* Hmm, if the data we parse is out of a certain range just consider it misparsed */
147 #define SK_MKELVIN_VALID_MIN ((uint64_t) ((-15LL*1000LL) + 273150LL))
148 #define SK_MKELVIN_VALID_MAX ((uint64_t) ((100LL*1000LL) + 273150LL))
150 #define SK_MSECOND_VALID_MIN 1ULL
151 #define SK_MSECOND_VALID_SHORT_MAX (60ULL * 60ULL * 1000ULL)
152 #define SK_MSECOND_VALID_LONG_MAX (30ULL * 365ULL * 24ULL * 60ULL * 60ULL * 1000ULL)
154 static int init_smart(SkDisk *d);
156 static const char *disk_type_to_human_string(SkDiskType type) {
158 /* %STRINGPOOLSTART% */
159 static const char* const map[_SK_DISK_TYPE_MAX] = {
160 [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = "16 Byte SCSI ATA SAT Passthru",
161 [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = "12 Byte SCSI ATA SAT Passthru",
162 [SK_DISK_TYPE_LINUX_IDE] = "Native Linux IDE",
163 [SK_DISK_TYPE_SUNPLUS] = "Sunplus SCSI ATA Passthru",
164 [SK_DISK_TYPE_JMICRON] = "JMicron SCSI ATA Passthru",
165 [SK_DISK_TYPE_BLOB] = "Blob",
166 [SK_DISK_TYPE_AUTO] = "Automatic",
167 [SK_DISK_TYPE_NONE] = "None"
169 /* %STRINGPOOLSTOP% */
171 if (type >= _SK_DISK_TYPE_MAX)
174 return _P(map[type]);
177 static const char *disk_type_to_prefix_string(SkDiskType type) {
179 /* %STRINGPOOLSTART% */
180 static const char* const map[_SK_DISK_TYPE_MAX] = {
181 [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = "sat16",
182 [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = "sat12",
183 [SK_DISK_TYPE_LINUX_IDE] = "linux-ide",
184 [SK_DISK_TYPE_SUNPLUS] = "sunplus",
185 [SK_DISK_TYPE_JMICRON] = "jmicron",
186 [SK_DISK_TYPE_NONE] = "none",
187 [SK_DISK_TYPE_AUTO] = "auto",
189 /* %STRINGPOOLSTOP% */
191 if (type >= _SK_DISK_TYPE_MAX)
194 return _P(map[type]);
197 static const char *disk_type_from_string(const char *s, SkDiskType *type) {
203 for (u = 0; u < _SK_DISK_TYPE_MAX; u++) {
207 if (!(t = disk_type_to_prefix_string(u)))
212 if (strncmp(s, t, l))
226 static SkBool disk_smart_is_available(SkDisk *d) {
227 return d->identify_valid && !!(d->identify[164] & 1);
230 static SkBool disk_smart_is_enabled(SkDisk *d) {
231 return d->identify_valid && !!(d->identify[170] & 1);
234 static SkBool disk_smart_is_conveyance_test_available(SkDisk *d) {
235 assert(d->smart_data_valid);
237 return !!(d->smart_data[367] & 32);
239 static SkBool disk_smart_is_short_and_extended_test_available(SkDisk *d) {
240 assert(d->smart_data_valid);
242 return !!(d->smart_data[367] & 16);
245 static SkBool disk_smart_is_start_test_available(SkDisk *d) {
246 assert(d->smart_data_valid);
248 return !!(d->smart_data[367] & 1);
251 static SkBool disk_smart_is_abort_test_available(SkDisk *d) {
252 assert(d->smart_data_valid);
254 return !!(d->smart_data[367] & 41);
257 static int disk_linux_ide_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
258 uint8_t *bytes = cmd_data;
261 assert(d->type == SK_DISK_TYPE_LINUX_IDE);
265 case SK_DIRECTION_OUT:
267 /* We could use HDIO_DRIVE_TASKFILE here, but
268 * that's a deprecated ioctl(), hence we don't
269 * do it. And we don't need writing anyway. */
274 case SK_DIRECTION_IN: {
277 /* We have HDIO_DRIVE_CMD which can only read, but not write,
278 * and cannot do LBA. We use it for all read commands. */
280 ioctl_data = alloca(4 + *len);
281 memset(ioctl_data, 0, 4 + *len);
283 ioctl_data[0] = (uint8_t) command; /* COMMAND */
284 ioctl_data[1] = ioctl_data[0] == WIN_SMART ? bytes[9] : bytes[3]; /* SECTOR/NSECTOR */
285 ioctl_data[2] = bytes[1]; /* FEATURE */
286 ioctl_data[3] = bytes[3]; /* NSECTOR */
288 if ((ret = ioctl(d->fd, HDIO_DRIVE_CMD, ioctl_data)) < 0)
291 memset(bytes, 0, 12);
292 bytes[11] = ioctl_data[0];
293 bytes[1] = ioctl_data[1];
294 bytes[3] = ioctl_data[2];
296 memcpy(data, ioctl_data+4, *len);
301 case SK_DIRECTION_NONE: {
302 uint8_t ioctl_data[7];
304 /* We have HDIO_DRIVE_TASK which can neither read nor
305 * write, but can do LBA. We use it for all commands that
306 * do neither read nor write */
308 memset(ioctl_data, 0, sizeof(ioctl_data));
310 ioctl_data[0] = (uint8_t) command; /* COMMAND */
311 ioctl_data[1] = bytes[1]; /* FEATURE */
312 ioctl_data[2] = bytes[3]; /* NSECTOR */
314 ioctl_data[3] = bytes[9]; /* LBA LOW */
315 ioctl_data[4] = bytes[8]; /* LBA MID */
316 ioctl_data[5] = bytes[7]; /* LBA HIGH */
317 ioctl_data[6] = bytes[10]; /* SELECT */
319 if ((ret = ioctl(d->fd, HDIO_DRIVE_TASK, ioctl_data)))
322 memset(bytes, 0, 12);
323 bytes[11] = ioctl_data[0];
324 bytes[1] = ioctl_data[1];
325 bytes[3] = ioctl_data[2];
327 bytes[9] = ioctl_data[3];
328 bytes[8] = ioctl_data[4];
329 bytes[7] = ioctl_data[5];
331 bytes[10] = ioctl_data[6];
342 /* Sends a SCSI command block */
343 static int sg_io(int fd, int direction,
344 const void *cdb, size_t cdb_len,
345 void *data, size_t data_len,
346 void *sense, size_t sense_len) {
348 struct sg_io_hdr io_hdr;
350 memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
352 io_hdr.interface_id = 'S';
353 io_hdr.cmdp = (unsigned char*) cdb;
354 io_hdr.cmd_len = cdb_len;
355 io_hdr.dxferp = data;
356 io_hdr.dxfer_len = data_len;
358 io_hdr.mx_sb_len = sense_len;
359 io_hdr.dxfer_direction = direction;
360 io_hdr.timeout = SK_TIMEOUT;
362 return ioctl(fd, SG_IO, &io_hdr);
365 static int disk_passthrough_16_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
366 uint8_t *bytes = cmd_data;
369 uint8_t *desc = sense+8;
372 static const int direction_map[] = {
373 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
374 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
375 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
378 assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_16);
380 /* ATA Pass-Through 16 byte command, as described in "T10 04-262r8
381 * ATA Command Pass-Through":
382 * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */
384 memset(cdb, 0, sizeof(cdb));
386 cdb[0] = 0x85; /* OPERATION CODE: 16 byte pass through */
388 if (direction == SK_DIRECTION_NONE) {
389 cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */
390 cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */
392 } else if (direction == SK_DIRECTION_IN) {
393 cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
394 cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
396 } else if (direction == SK_DIRECTION_OUT) {
397 cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */
398 cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */
401 cdb[3] = bytes[0]; /* FEATURES */
404 cdb[5] = bytes[2]; /* SECTORS */
407 cdb[8] = bytes[9]; /* LBA LOW */
408 cdb[10] = bytes[8]; /* LBA MID */
409 cdb[12] = bytes[7]; /* LBA HIGH */
411 cdb[13] = bytes[10] & 0x4F; /* SELECT */
412 cdb[14] = (uint8_t) command;
414 memset(sense, 0, sizeof(sense));
416 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
419 if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) {
424 memset(bytes, 0, 12);
432 bytes[10] = desc[12];
433 bytes[11] = desc[13];
438 static int disk_passthrough_12_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
439 uint8_t *bytes = cmd_data;
442 uint8_t *desc = sense+8;
445 static const int direction_map[] = {
446 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
447 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
448 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
451 assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12);
453 /* ATA Pass-Through 12 byte command, as described in "T10 04-262r8
454 * ATA Command Pass-Through":
455 * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */
457 memset(cdb, 0, sizeof(cdb));
459 cdb[0] = 0xa1; /* OPERATION CODE: 12 byte pass through */
461 if (direction == SK_DIRECTION_NONE) {
462 cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */
463 cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */
465 } else if (direction == SK_DIRECTION_IN) {
466 cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
467 cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
469 } else if (direction == SK_DIRECTION_OUT) {
470 cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */
471 cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */
474 cdb[3] = bytes[1]; /* FEATURES */
475 cdb[4] = bytes[3]; /* SECTORS */
477 cdb[5] = bytes[9]; /* LBA LOW */
478 cdb[6] = bytes[8]; /* LBA MID */
479 cdb[7] = bytes[7]; /* LBA HIGH */
481 cdb[8] = bytes[10] & 0x4F; /* SELECT */
482 cdb[9] = (uint8_t) command;
484 memset(sense, 0, sizeof(sense));
486 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
489 if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) {
494 memset(bytes, 0, 12);
496 bytes[1] = desc[3]; /* FEATURES */
497 bytes[2] = desc[4]; /* STATUS */
498 bytes[3] = desc[5]; /* SECTORS */
499 bytes[9] = desc[7]; /* LBA LOW */
500 bytes[8] = desc[9]; /* LBA MID */
501 bytes[7] = desc[11]; /* LBA HIGH */
502 bytes[10] = desc[12]; /* SELECT */
503 bytes[11] = desc[13]; /* ERROR */
508 static int disk_sunplus_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
509 uint8_t *bytes = cmd_data;
511 uint8_t sense[32], buf[8];
513 static const int direction_map[] = {
514 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
515 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
516 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
519 assert(d->type == SK_DISK_TYPE_SUNPLUS);
521 /* SunplusIT specific SCSI ATA pass-thru. Inspired by smartmonutils' support for these bridges */
523 memset(cdb, 0, sizeof(cdb));
525 cdb[0] = 0xF8; /* OPERATION CODE: Sunplus specific */
526 cdb[1] = 0x00; /* Subcommand: Pass-thru */
529 if (direction == SK_DIRECTION_NONE)
530 cdb[3] = 0x00; /* protocol */
531 else if (direction == SK_DIRECTION_IN)
532 cdb[3] = 0x10; /* protocol */
533 else if (direction == SK_DIRECTION_OUT)
534 cdb[3] = 0x11; /* protocol */
536 cdb[4] = bytes[3]; /* size? */
537 cdb[5] = bytes[1]; /* FEATURES */
538 cdb[6] = bytes[3]; /* SECTORS */
539 cdb[7] = bytes[9]; /* LBA LOW */
540 cdb[8] = bytes[8]; /* LBA MID */
541 cdb[9] = bytes[7]; /* LBA HIGH */
542 cdb[10] = bytes[10] | 0xA0; /* SELECT */
543 cdb[11] = (uint8_t) command;
545 memset(sense, 0, sizeof(sense));
548 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
551 memset(cdb, 0, sizeof(cdb));
557 memset(buf, 0, sizeof(buf));
559 /* Ask for response */
560 if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), buf, sizeof(buf), sense, sizeof(sense))) < 0)
563 memset(bytes, 0, 12);
565 bytes[2] = buf[1]; /* ERROR */
566 bytes[3] = buf[2]; /* SECTORS */
567 bytes[9] = buf[3]; /* LBA LOW */
568 bytes[8] = buf[4]; /* LBA MID */
569 bytes[7] = buf[5]; /* LBA HIGH */
570 bytes[10] = buf[6]; /* SELECT */
571 bytes[11] = buf[7]; /* STATUS */
576 static int disk_jmicron_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* _data, size_t *_len) {
577 uint8_t *bytes = cmd_data;
582 SkBool is_smart_status = FALSE;
584 size_t len = _len ? *_len : 0;
585 uint8_t smart_status = 0;
587 static const int direction_map[] = {
588 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
589 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
590 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
593 assert(d->type == SK_DISK_TYPE_JMICRON);
595 /* JMicron specific SCSI ATA pass-thru. Inspired by smartmonutils' support for these bridges */
597 memset(cdb, 0, sizeof(cdb));
599 cdb[0] = 0xdf; /* operation code */
602 cdb[3] = 0x00; /* size HI */
603 cdb[4] = sizeof(port); /* size LO */
605 cdb[6] = 0x72; /* register address HI */
606 cdb[7] = 0x0f; /* register address LO */
612 memset(sense, 0, sizeof(sense));
614 if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), &port, sizeof(port), sense, sizeof(sense))) < 0)
617 /* Port & 0x04 is port #0, Port & 0x40 is port #1 */
621 cdb[0] = 0xdf; /* OPERATION CODE: 12 byte pass through */
623 if (command == SK_ATA_COMMAND_SMART && bytes[1] == SK_SMART_COMMAND_RETURN_STATUS) {
624 /* We need to rewrite the SMART status request */
625 is_smart_status = TRUE;
626 direction = SK_DIRECTION_IN;
627 data = &smart_status;
628 len = sizeof(smart_status);
630 } else if (direction == SK_DIRECTION_NONE)
632 else if (direction == SK_DIRECTION_IN)
634 else if (direction == SK_DIRECTION_OUT)
639 cdb[3] = (uint8_t) (len >> 8);
640 cdb[4] = (uint8_t) (len & 0xFF);
642 cdb[5] = bytes[1]; /* FEATURES */
643 cdb[6] = bytes[3]; /* SECTORS */
645 cdb[7] = bytes[9]; /* LBA LOW */
646 cdb[8] = bytes[8]; /* LBA MID */
647 cdb[9] = bytes[7]; /* LBA HIGH */
649 cdb[10] = bytes[10] | ((port & 0x04) ? 0xA0 : 0xB0); /* SELECT */
650 cdb[11] = (uint8_t) command;
652 memset(sense, 0, sizeof(sense));
654 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len, sense, sizeof(sense))) < 0)
657 memset(bytes, 0, 12);
659 if (is_smart_status) {
660 if (smart_status == 0x01 || smart_status == 0xc2) {
661 bytes[7] = 0xc2; /* LBA HIGH */
662 bytes[8] = 0x4f; /* LBA MID */
663 } else if (smart_status == 0x00 || smart_status == 0x2c) {
664 bytes[7] = 0x2c; /* LBA HIGH */
665 bytes[8] = 0xf4; /* LBA MID */
671 cdb[0] = 0xdf; /* operation code */
674 cdb[3] = 0x00; /* size HI */
675 cdb[4] = sizeof(regbuf); /* size LO */
677 cdb[6] = (port & 0x04) ? 0x80 : 0x90; /* register address HI */
678 cdb[7] = 0x00; /* register address LO */
684 if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), regbuf, sizeof(regbuf), sense, sizeof(sense))) < 0)
687 bytes[2] = regbuf[14]; /* STATUS */
688 bytes[3] = regbuf[0]; /* SECTORS */
689 bytes[9] = regbuf[6]; /* LBA LOW */
690 bytes[8] = regbuf[4]; /* LBA MID */
691 bytes[7] = regbuf[10]; /* LBA HIGH */
692 bytes[10] = regbuf[9]; /* SELECT */
693 bytes[11] = regbuf[13]; /* ERROR */
699 static int disk_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
701 static int (* const disk_command_table[_SK_DISK_TYPE_MAX]) (SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) = {
702 [SK_DISK_TYPE_LINUX_IDE] = disk_linux_ide_command,
703 [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = disk_passthrough_12_command,
704 [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = disk_passthrough_16_command,
705 [SK_DISK_TYPE_SUNPLUS] = disk_sunplus_command,
706 [SK_DISK_TYPE_JMICRON] = disk_jmicron_command,
707 [SK_DISK_TYPE_BLOB] = NULL,
708 [SK_DISK_TYPE_AUTO] = NULL,
709 [SK_DISK_TYPE_NONE] = NULL
713 assert(d->type <= _SK_DISK_TYPE_MAX);
714 assert(direction <= _SK_DIRECTION_MAX);
716 assert(direction == SK_DIRECTION_NONE || (data && len && *len > 0));
717 assert(direction != SK_DIRECTION_NONE || (!data && !len));
719 if (!disk_command_table[d->type]) {
724 return disk_command_table[d->type](d, command, direction, cmd_data, data, len);
727 static int disk_identify_device(SkDisk *d) {
733 if (d->type == SK_DISK_TYPE_BLOB)
736 memset(d->identify, 0, len);
737 memset(cmd, 0, sizeof(cmd));
741 if ((ret = disk_command(d, SK_ATA_COMMAND_IDENTIFY_DEVICE, SK_DIRECTION_IN, cmd, d->identify, &len)) < 0)
749 /* Check if IDENTIFY data is all NULs */
750 for (p = d->identify; p < (const uint8_t*) d->identify+len; p++)
761 d->identify_valid = TRUE;
766 int sk_disk_check_sleep_mode(SkDisk *d, SkBool *awake) {
771 if (!d->identify_valid) {
776 if (d->type == SK_DISK_TYPE_BLOB) {
781 memset(cmd, 0, sizeof(cmd));
783 if ((ret = disk_command(d, SK_ATA_COMMAND_CHECK_POWER_MODE, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
786 if (cmd[0] != 0 || (ntohs(cmd[5]) & 1) != 0) {
791 status = ntohs(cmd[1]) & 0xFF;
792 *awake = status == 0xFF || status == 0x80; /* idle and active/idle is considered awake */
797 static int disk_smart_enable(SkDisk *d, SkBool b) {
800 if (!disk_smart_is_available(d)) {
805 if (d->type == SK_DISK_TYPE_BLOB) {
810 memset(cmd, 0, sizeof(cmd));
812 cmd[0] = htons(b ? SK_SMART_COMMAND_ENABLE_OPERATIONS : SK_SMART_COMMAND_DISABLE_OPERATIONS);
813 cmd[2] = htons(0x0000U);
814 cmd[3] = htons(0x00C2U);
815 cmd[4] = htons(0x4F00U);
817 return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0);
820 int sk_disk_smart_read_data(SkDisk *d) {
825 if (init_smart(d) < 0)
828 if (!disk_smart_is_available(d)) {
833 if (d->type == SK_DISK_TYPE_BLOB)
836 memset(cmd, 0, sizeof(cmd));
838 cmd[0] = htons(SK_SMART_COMMAND_READ_DATA);
840 cmd[2] = htons(0x0000U);
841 cmd[3] = htons(0x00C2U);
842 cmd[4] = htons(0x4F00U);
844 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_data, &len)) < 0)
847 d->smart_data_valid = TRUE;
852 static int disk_smart_read_thresholds(SkDisk *d) {
857 if (!disk_smart_is_available(d)) {
862 if (d->type == SK_DISK_TYPE_BLOB)
865 memset(cmd, 0, sizeof(cmd));
867 cmd[0] = htons(SK_SMART_COMMAND_READ_THRESHOLDS);
869 cmd[2] = htons(0x0000U);
870 cmd[3] = htons(0x00C2U);
871 cmd[4] = htons(0x4F00U);
873 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_thresholds, &len)) < 0)
876 d->smart_thresholds_valid = TRUE;
881 int sk_disk_smart_status(SkDisk *d, SkBool *good) {
885 if (init_smart(d) < 0)
888 if (!disk_smart_is_available(d)) {
893 if (d->type == SK_DISK_TYPE_BLOB) {
895 if (d->blob_smart_status_valid) {
896 *good = d->blob_smart_status;
904 memset(cmd, 0, sizeof(cmd));
906 cmd[0] = htons(SK_SMART_COMMAND_RETURN_STATUS);
907 cmd[1] = htons(0x0000U);
908 cmd[3] = htons(0x00C2U);
909 cmd[4] = htons(0x4F00U);
911 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
914 /* SAT/USB bridges truncate packets, so we only check for 4F,
915 * not for 2C on those */
916 if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x00C2U)) &&
917 cmd[4] == htons(0x4F00U))
919 else if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x002CU)) &&
920 cmd[4] == htons(0xF400U))
930 int sk_disk_smart_self_test(SkDisk *d, SkSmartSelfTest test) {
934 if (init_smart(d) < 0)
937 if (!disk_smart_is_available(d)) {
942 if (d->type == SK_DISK_TYPE_BLOB) {
947 if (!d->smart_data_valid)
948 if ((ret = sk_disk_smart_read_data(d)) < 0)
951 assert(d->smart_data_valid);
953 if (test != SK_SMART_SELF_TEST_SHORT &&
954 test != SK_SMART_SELF_TEST_EXTENDED &&
955 test != SK_SMART_SELF_TEST_CONVEYANCE &&
956 test != SK_SMART_SELF_TEST_ABORT) {
961 if (!disk_smart_is_start_test_available(d)
962 || (test == SK_SMART_SELF_TEST_ABORT && !disk_smart_is_abort_test_available(d))
963 || ((test == SK_SMART_SELF_TEST_SHORT || test == SK_SMART_SELF_TEST_EXTENDED) && !disk_smart_is_short_and_extended_test_available(d))
964 || (test == SK_SMART_SELF_TEST_CONVEYANCE && !disk_smart_is_conveyance_test_available(d))) {
969 if (test == SK_SMART_SELF_TEST_ABORT &&
970 !disk_smart_is_abort_test_available(d)) {
975 memset(cmd, 0, sizeof(cmd));
977 cmd[0] = htons(SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE);
978 cmd[2] = htons(0x0000U);
979 cmd[3] = htons(0x00C2U);
980 cmd[4] = htons(0x4F00U | (uint16_t) test);
982 return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, NULL);
985 static void swap_strings(char *s, size_t len) {
986 assert((len & 1) == 0);
988 for (; len > 0; s += 2, len -= 2) {
996 static void clean_strings(char *s) {
1000 if (*e < ' ' || *e >= 127)
1004 static void drop_spaces(char *s) {
1006 SkBool prev_space = FALSE;
1008 s += strspn(s, " ");
1029 static void read_string(char *d, uint8_t *s, size_t len) {
1032 swap_strings(d, len);
1037 int sk_disk_identify_parse(SkDisk *d, const SkIdentifyParsedData **ipd) {
1041 if (!d->identify_valid) {
1046 read_string(d->identify_parsed_data.serial, d->identify+20, 20);
1047 read_string(d->identify_parsed_data.firmware, d->identify+46, 8);
1048 read_string(d->identify_parsed_data.model, d->identify+54, 40);
1050 *ipd = &d->identify_parsed_data;
1055 int sk_disk_smart_is_available(SkDisk *d, SkBool *b) {
1059 if (!d->identify_valid) {
1064 *b = disk_smart_is_available(d);
1068 int sk_disk_identify_is_available(SkDisk *d, SkBool *b) {
1072 *b = d->identify_valid;
1076 const char *sk_smart_offline_data_collection_status_to_string(SkSmartOfflineDataCollectionStatus status) {
1078 /* %STRINGPOOLSTART% */
1079 static const char* const map[] = {
1080 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER] = "Off-line data collection activity was never started.",
1081 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS] = "Off-line data collection activity was completed without error.",
1082 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS] = "Off-line activity in progress.",
1083 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED] = "Off-line data collection activity was suspended by an interrupting command from host.",
1084 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED] = "Off-line data collection activity was aborted by an interrupting command from host.",
1085 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL] = "Off-line data collection activity was aborted by the device with a fatal error.",
1086 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN] = "Unknown status"
1088 /* %STRINGPOOLSTOP% */
1090 if (status >= _SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_MAX)
1093 return _P(map[status]);
1096 const char *sk_smart_self_test_execution_status_to_string(SkSmartSelfTestExecutionStatus status) {
1098 /* %STRINGPOOLSTART% */
1099 static const char* const map[] = {
1100 [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.",
1101 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED] = "The self-test routine was aborted by the host.",
1102 [SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED] = "The self-test routine was interrupted by the host with a hardware or software reset.",
1103 [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.",
1104 [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.",
1105 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL] = "The previous self-test completed having the electrical element of the test failed.",
1106 [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.",
1107 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ] = "The previous self-test completed having the read element of the test failed.",
1108 [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.",
1109 [SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS] = "Self-test routine in progress"
1111 /* %STRINGPOOLSTOP% */
1113 if (status >= _SK_SMART_SELF_TEST_EXECUTION_STATUS_MAX)
1116 return _P(map[status]);
1119 const char* sk_smart_self_test_to_string(SkSmartSelfTest test) {
1122 case SK_SMART_SELF_TEST_SHORT:
1124 case SK_SMART_SELF_TEST_EXTENDED:
1126 case SK_SMART_SELF_TEST_CONVEYANCE:
1127 return "conveyance";
1128 case SK_SMART_SELF_TEST_ABORT:
1135 SkBool sk_smart_self_test_available(const SkSmartParsedData *d, SkSmartSelfTest test) {
1138 if (!d->start_test_available)
1142 case SK_SMART_SELF_TEST_SHORT:
1143 case SK_SMART_SELF_TEST_EXTENDED:
1144 return d->short_and_extended_test_available;
1145 case SK_SMART_SELF_TEST_CONVEYANCE:
1146 return d->conveyance_test_available;
1147 case SK_SMART_SELF_TEST_ABORT:
1148 return d->abort_test_available;
1154 unsigned sk_smart_self_test_polling_minutes(const SkSmartParsedData *d, SkSmartSelfTest test) {
1157 if (!sk_smart_self_test_available(d, test))
1161 case SK_SMART_SELF_TEST_SHORT:
1162 return d->short_test_polling_minutes;
1163 case SK_SMART_SELF_TEST_EXTENDED:
1164 return d->extended_test_polling_minutes;
1165 case SK_SMART_SELF_TEST_CONVEYANCE:
1166 return d->conveyance_test_polling_minutes;
1172 static void make_pretty(SkSmartAttributeParsedData *a) {
1173 uint64_t fourtyeight;
1178 if (a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_UNKNOWN)
1182 ((uint64_t) a->raw[0]) |
1183 (((uint64_t) a->raw[1]) << 8) |
1184 (((uint64_t) a->raw[2]) << 16) |
1185 (((uint64_t) a->raw[3]) << 24) |
1186 (((uint64_t) a->raw[4]) << 32) |
1187 (((uint64_t) a->raw[5]) << 40);
1189 if (!strcmp(a->name, "spin-up-time"))
1190 a->pretty_value = fourtyeight & 0xFFFF;
1191 else if (!strcmp(a->name, "airflow-temperature-celsius") ||
1192 !strcmp(a->name, "temperature-celsius") ||
1193 !strcmp(a->name, "temperature-celsius-2"))
1194 a->pretty_value = (fourtyeight & 0xFFFF)*1000 + 273150;
1195 else if (!strcmp(a->name, "temperature-centi-celsius"))
1196 a->pretty_value = (fourtyeight & 0xFFFF)*100 + 273150;
1197 else if (!strcmp(a->name, "power-on-minutes"))
1198 a->pretty_value = fourtyeight * 60 * 1000;
1199 else if (!strcmp(a->name, "power-on-seconds") ||
1200 !strcmp(a->name, "power-on-seconds-2"))
1201 a->pretty_value = fourtyeight * 1000;
1202 else if (!strcmp(a->name, "power-on-half-minutes"))
1203 a->pretty_value = fourtyeight * 30 * 1000;
1204 else if (!strcmp(a->name, "power-on-hours") ||
1205 !strcmp(a->name, "loaded-hours") ||
1206 !strcmp(a->name, "head-flying-hours"))
1207 a->pretty_value = (fourtyeight & 0xFFFFFFFFU) * 60 * 60 * 1000;
1208 else if (!strcmp(a->name, "reallocated-sector-count") ||
1209 !strcmp(a->name, "current-pending-sector"))
1210 a->pretty_value = fourtyeight & 0xFFFFFFFFU;
1211 else if (!strcmp(a->name, "endurance-remaining") ||
1212 !strcmp(a->name, "available-reserved-space"))
1213 a->pretty_value = a->current_value;
1214 else if (!strcmp(a->name, "total-lbas-written") ||
1215 !strcmp(a->name, "total-lbas-read"))
1216 a->pretty_value = fourtyeight * 65536LLU * 512LLU / 1000000LLU;
1217 else if (!strcmp(a->name, "timed-workload-media-wear") ||
1218 !strcmp(a->name, "timed-workload-host-reads"))
1219 a->pretty_value = (double)fourtyeight / 1024LLU;
1220 else if (!strcmp(a->name, "workload-timer"))
1221 a->pretty_value = fourtyeight * 60 * 1000;
1223 a->pretty_value = fourtyeight;
1226 typedef void (*SkSmartAttributeVerify)(SkDisk *d, SkSmartAttributeParsedData *a);
1228 typedef struct SkSmartAttributeInfo {
1230 SkSmartAttributeUnit unit;
1231 SkSmartAttributeVerify verify;
1232 } SkSmartAttributeInfo;
1234 static void verify_temperature(SkDisk *d, SkSmartAttributeParsedData *a) {
1236 assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_MKELVIN);
1238 if (a->pretty_value < SK_MKELVIN_VALID_MIN ||
1239 a->pretty_value > SK_MKELVIN_VALID_MAX) {
1240 a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1241 d->attribute_verification_bad = TRUE;
1245 static void verify_short_time(SkDisk *d, SkSmartAttributeParsedData *a) {
1247 assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_MSECONDS);
1249 if (a->pretty_value < SK_MSECOND_VALID_MIN ||
1250 a->pretty_value > SK_MSECOND_VALID_SHORT_MAX) {
1251 a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1252 d->attribute_verification_bad = TRUE;
1256 static void verify_long_time(SkDisk *d, SkSmartAttributeParsedData *a) {
1258 assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_MSECONDS);
1260 if (a->pretty_value < SK_MSECOND_VALID_MIN ||
1261 a->pretty_value > SK_MSECOND_VALID_LONG_MAX) {
1262 a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1263 d->attribute_verification_bad = TRUE;
1267 static void verify_sectors(SkDisk *d, SkSmartAttributeParsedData *a) {
1268 uint64_t max_sectors;
1272 assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_SECTORS);
1274 max_sectors = d->size / 512ULL;
1276 if (a->pretty_value == 0xffffffffULL ||
1277 a->pretty_value == 0xffffffffffffffffULL ||
1278 (max_sectors > 0 && a->pretty_value > max_sectors)) {
1279 a->pretty_value = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1280 d->attribute_verification_bad = TRUE;
1282 if ((!strcmp(a->name, "reallocated-sector-count") ||
1283 !strcmp(a->name, "current-pending-sector")) &&
1284 a->pretty_value > 0)
1289 /* This data is stolen from smartmontools */
1291 /* %STRINGPOOLSTART% */
1292 static const SkSmartAttributeInfo const attribute_info[256] = {
1293 [1] = { "raw-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1294 [2] = { "throughput-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1295 [3] = { "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_short_time },
1296 [4] = { "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1297 [5] = { "reallocated-sector-count", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1298 [6] = { "read-channel-margin", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1299 [7] = { "seek-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1300 [8] = { "seek-time-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1301 [9] = { "power-on-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1302 [10] = { "spin-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1303 [11] = { "calibration-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1304 [12] = { "power-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1305 [13] = { "read-soft-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1306 [170] = { "available-reserved-space", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL },
1307 [171] = { "program-fail-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1308 [172] = { "erase-fail-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1309 [184] = { "end-to-end-error", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1310 [187] = { "reported-uncorrect", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1311 [188] = { "command-timeout", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1312 [189] = { "high-fly-writes", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1313 [190] = { "airflow-temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1314 [191] = { "g-sense-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1315 [192] = { "power-off-retract-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1316 [193] = { "load-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1317 [194] = { "temperature-celsius-2", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1318 [195] = { "hardware-ecc-recovered", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1319 [196] = { "reallocated-event-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1320 [197] = { "current-pending-sector", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1321 [198] = { "offline-uncorrectable", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1322 [199] = { "udma-crc-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1323 [200] = { "multi-zone-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1324 [201] = { "soft-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1325 [202] = { "ta-increase-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1326 [203] = { "run-out-cancel", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1327 [204] = { "shock-count-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1328 [205] = { "shock-rate-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1329 [206] = { "flying-height", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1330 [207] = { "spin-high-current", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1331 [208] = { "spin-buzz", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1332 [209] = { "offline-seek-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1333 [220] = { "disk-shift", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1334 [221] = { "g-sense-error-rate-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1335 [222] = { "loaded-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1336 [223] = { "load-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1337 [224] = { "load-friction", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1338 [225] = { "load-cycle-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1339 [226] = { "load-in-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_short_time },
1340 [227] = { "torq-amp-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1341 [228] = { "power-off-retract-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1342 [230] = { "head-amplitude", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1343 [231] = { "temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1345 /* http://www.adtron.com/pdf/SMART_for_XceedLite_SATA_RevA.pdf */
1346 [232] = { "endurance-remaining", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL },
1347 [233] = { "power-on-seconds-2", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1348 [234] = { "uncorrectable-ecc-count", SK_SMART_ATTRIBUTE_UNIT_SECTORS, NULL },
1349 [235] = { "good-block-rate", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1351 [240] = { "head-flying-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1352 [241] = { "total-lbas-written", SK_SMART_ATTRIBUTE_UNIT_MB, NULL },
1353 [242] = { "total-lbas-read", SK_SMART_ATTRIBUTE_UNIT_MB, NULL },
1354 [250] = { "read-error-retry-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }
1356 /* %STRINGPOOLSTOP% */
1358 typedef enum SkSmartQuirk {
1359 SK_SMART_QUIRK_9_POWERONMINUTES = 0x000001,
1360 SK_SMART_QUIRK_9_POWERONSECONDS = 0x000002,
1361 SK_SMART_QUIRK_9_POWERONHALFMINUTES = 0x000004,
1362 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT = 0x000008,
1363 SK_SMART_QUIRK_193_LOADUNLOAD = 0x000010,
1364 SK_SMART_QUIRK_194_10XCELSIUS = 0x000020,
1365 SK_SMART_QUIRK_194_UNKNOWN = 0x000040,
1366 SK_SMART_QUIRK_200_WRITEERRORCOUNT = 0x000080,
1367 SK_SMART_QUIRK_201_DETECTEDTACOUNT = 0x000100,
1368 SK_SMART_QUIRK_5_UNKNOWN = 0x000200,
1369 SK_SMART_QUIRK_9_UNKNOWN = 0x000400,
1370 SK_SMART_QUIRK_197_UNKNOWN = 0x000800,
1371 SK_SMART_QUIRK_198_UNKNOWN = 0x001000,
1372 SK_SMART_QUIRK_190_UNKNOWN = 0x002000,
1373 SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE = 0x004000,
1374 SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR = 0x008000,
1375 SK_SMART_QUIRK_225_TOTALLBASWRITTEN = 0x010000,
1376 SK_SMART_QUIRK_4_UNUSED = 0x020000,
1377 SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR = 0x040000,
1378 SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS = 0x080000,
1379 SK_SMART_QUIRK_228_WORKLOADTIMER = 0x100000,
1380 SK_SMART_QUIRK_3_UNUSED = 0x200000
1383 /* %STRINGPOOLSTART% */
1384 static const char *quirk_name[] = {
1387 "9_POWERONHALFMINUTES",
1388 "192_EMERGENCYRETRACTCYCLECT",
1392 "200_WRITEERRORCOUNT",
1393 "201_DETECTEDTACOUNT",
1399 "232_AVAILABLERESERVEDSPACE",
1400 "233_MEDIAWEAROUTINDICATOR",
1401 "225_TOTALLBASWRITTEN",
1403 "226_TIMEWORKLOADMEDIAWEAR",
1404 "227_TIMEWORKLOADHOSTREADS",
1405 "228_WORKLOADTIMER",
1409 /* %STRINGPOOLSTOP% */
1411 typedef struct SkSmartQuirkDatabase {
1413 const char *firmware;
1415 } SkSmartQuirkDatabase;
1417 static const SkSmartQuirkDatabase quirk_database[] = { {
1421 "FUJITSU MHY2120BH|"
1424 "^0085000B$", /* seems to be specific to this firmware */
1425 SK_SMART_QUIRK_9_POWERONMINUTES|
1426 SK_SMART_QUIRK_197_UNKNOWN|
1427 SK_SMART_QUIRK_198_UNKNOWN
1429 "^FUJITSU MHR2040AT$",
1431 SK_SMART_QUIRK_9_POWERONSECONDS|
1432 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1433 SK_SMART_QUIRK_200_WRITEERRORCOUNT
1435 "^FUJITSU MHS20[6432]0AT( .)?$",
1437 SK_SMART_QUIRK_9_POWERONSECONDS|
1438 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1439 SK_SMART_QUIRK_200_WRITEERRORCOUNT|
1440 SK_SMART_QUIRK_201_DETECTEDTACOUNT
1444 "FUJITSU MHG2...ATU?.*|"
1445 "FUJITSU MHH2...ATU?.*|"
1446 "FUJITSU MHJ2...ATU?.*|"
1447 "FUJITSU MHK2...ATU?.*|"
1448 "FUJITSU MHL2300AT|"
1449 "FUJITSU MHM2(20|15|10|06)0AT|"
1450 "FUJITSU MHN2...AT|"
1451 "FUJITSU MHR2020AT|"
1452 "FUJITSU MHT2...(AH|AS|AT|BH)U?.*|"
1453 "FUJITSU MHU2...ATU?.*|"
1454 "FUJITSU MHV2...(AH|AS|AT|BH|BS|BT).*|"
1455 "FUJITSU MP[A-G]3...A[HTEV]U?.*"
1458 SK_SMART_QUIRK_9_POWERONSECONDS
1464 "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]"
1467 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1474 SK_SMART_QUIRK_9_POWERONHALFMINUTES|
1475 SK_SMART_QUIRK_194_10XCELSIUS
1477 "^SAMSUNG SP40A2H$",
1479 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1481 "^SAMSUNG SP80A4H$",
1483 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1485 "^SAMSUNG SP8004H$",
1487 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1492 "Maxtor 2B0(0[468]|1[05]|20)H1|"
1493 "Maxtor 4G(120J6|160J[68])|"
1494 "Maxtor 4D0(20H1|40H2|60H3|80H4)"
1497 SK_SMART_QUIRK_9_POWERONMINUTES|
1498 SK_SMART_QUIRK_194_UNKNOWN
1501 "Maxtor 2F0[234]0[JL]0|"
1502 "Maxtor 8(1280A2|2160A4|2560A4|3840A6|4000A6|5120A8)|"
1503 "Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)|"
1504 "Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)|"
1505 "Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)|"
1506 "Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)|"
1507 "Maxtor 9(2732U8|2390U7|204[09]U6|1707U5|1366U4|1024U3|0845U3|0683U2)|"
1508 "Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)|"
1509 "Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D[34]|90432D2)|"
1510 "Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)|"
1511 "Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)|"
1512 "Maxtor (33073U4|32049U3|31536U2|30768U1|33073H4|32305H3|31536H2|30768H1)|"
1513 "Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)|"
1514 "Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)|"
1515 "Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)|"
1516 "Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?|"
1517 "Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)|"
1518 "Maxtor 9(1023U2|1536U2|2049U3|2305U3|3073U4|4610U6|6147U8)|"
1519 "Maxtor 9(1023H2|1536H2|2049H3|2305H3|3073H4|4098H6|4610H6|6147H8)|"
1520 "Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)|"
1521 "Maxtor (98196H8|96147H6)|"
1522 "Maxtor 4W(100H6|080H6|060H4|040H3|030H2)|"
1523 "Maxtor 6(E0[234]|K04)0L0|"
1524 "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|"
1525 "Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)|"
1526 "Maxtor 7Y250[PM]0|"
1527 "Maxtor [45]A(25|30|32)0[JN]0|"
1528 "Maxtor 7L(25|30)0[SR]0"
1531 SK_SMART_QUIRK_9_POWERONMINUTES
1537 "HITACHI_DK14FA-20B|"
1538 "HITACHI_DK23..-..B?|"
1539 "HITACHI_DK23FA-20J|HTA422020F9AT[JN]0|"
1540 "HE[JN]4230[23]0F9AT00|"
1541 "HTC4260[23]0G5CE00|HTC4260[56]0G8CE00"
1544 SK_SMART_QUIRK_9_POWERONMINUTES|
1545 SK_SMART_QUIRK_193_LOADUNLOAD
1547 "^HTS541010G9SA00$",
1549 SK_SMART_QUIRK_5_UNKNOWN
1552 /*** Apple SSD (?) http://bugs.freedesktop.org/show_bug.cgi?id=24700
1553 https://bugs.launchpad.net/ubuntu/+source/gnome-disk-utility/+bug/438136/comments/4 */
1556 SK_SMART_QUIRK_5_UNKNOWN|
1557 SK_SMART_QUIRK_190_UNKNOWN
1561 "^INTEL SSDSA2CW[0-9]{3}G3$",
1563 SK_SMART_QUIRK_3_UNUSED|
1564 SK_SMART_QUIRK_4_UNUSED|
1565 SK_SMART_QUIRK_225_TOTALLBASWRITTEN|
1566 SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR|
1567 SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS|
1568 SK_SMART_QUIRK_228_WORKLOADTIMER|
1569 SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE|
1570 SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR
1578 static int match(const char*regex, const char *s, SkBool *result) {
1584 if (regcomp(&re, regex, REG_EXTENDED|REG_NOSUB) != 0) {
1589 if ((k = regexec(&re, s, 0, NULL, 0)) != 0) {
1591 if (k != REG_NOMATCH) {
1605 static int lookup_quirks(const char *model, const char *firmware, SkSmartQuirk *quirk) {
1607 const SkSmartQuirkDatabase *db;
1611 for (db = quirk_database; db->model || db->firmware; db++) {
1614 SkBool matching = FALSE;
1616 if ((k = match(db->model, model, &matching)) < 0)
1624 SkBool matching = FALSE;
1626 if ((k = match(db->firmware, firmware, &matching)) < 0)
1640 static const SkSmartAttributeInfo *lookup_attribute(SkDisk *d, uint8_t id) {
1641 const SkIdentifyParsedData *ipd;
1642 SkSmartQuirk quirk = 0;
1644 /* These are the complex ones */
1645 if (sk_disk_identify_parse(d, &ipd) < 0)
1648 if (lookup_quirks(ipd->model, ipd->firmware, &quirk) < 0)
1654 /* %STRINGPOOLSTART% */
1655 if (quirk & SK_SMART_QUIRK_3_UNUSED) {
1656 static const SkSmartAttributeInfo a = {
1657 "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL
1661 /* %STRINGPOOLSTOP% */
1666 /* %STRINGPOOLSTART% */
1667 if (quirk & SK_SMART_QUIRK_4_UNUSED) {
1668 static const SkSmartAttributeInfo a = {
1669 "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL
1673 /* %STRINGPOOLSTOP% */
1678 if (quirk & SK_SMART_QUIRK_5_UNKNOWN)
1684 /* %STRINGPOOLSTART% */
1685 if (quirk & SK_SMART_QUIRK_9_POWERONMINUTES) {
1686 static const SkSmartAttributeInfo a = {
1687 "power-on-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1691 } else if (quirk & SK_SMART_QUIRK_9_POWERONSECONDS) {
1692 static const SkSmartAttributeInfo a = {
1693 "power-on-seconds", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1697 } else if (quirk & SK_SMART_QUIRK_9_POWERONHALFMINUTES) {
1698 static const SkSmartAttributeInfo a = {
1699 "power-on-half-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1702 } else if (quirk & SK_SMART_QUIRK_9_UNKNOWN)
1704 /* %STRINGPOOLSTOP% */
1709 if (quirk & SK_SMART_QUIRK_190_UNKNOWN)
1715 /* %STRINGPOOLSTART% */
1716 if (quirk & SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) {
1717 static const SkSmartAttributeInfo a = {
1718 "emergency-retract-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1722 /* %STRINGPOOLSTOP% */
1727 /* %STRINGPOOLSTART% */
1728 if (quirk & SK_SMART_QUIRK_194_10XCELSIUS) {
1729 static const SkSmartAttributeInfo a = {
1730 "temperature-centi-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature
1733 } else if (quirk & SK_SMART_QUIRK_194_UNKNOWN)
1735 /* %STRINGPOOLSTOP% */
1740 if (quirk & SK_SMART_QUIRK_197_UNKNOWN)
1746 if (quirk & SK_SMART_QUIRK_198_UNKNOWN)
1752 /* %STRINGPOOLSTART% */
1753 if (quirk & SK_SMART_QUIRK_200_WRITEERRORCOUNT) {
1754 static const SkSmartAttributeInfo a = {
1755 "write-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1759 /* %STRINGPOOLSTOP% */
1764 /* %STRINGPOOLSTART% */
1765 if (quirk & SK_SMART_QUIRK_201_DETECTEDTACOUNT) {
1766 static const SkSmartAttributeInfo a = {
1767 "detected-ta-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1771 /* %STRINGPOOLSTOP% */
1776 /* %STRINGPOOLSTART% */
1777 if (quirk & SK_SMART_QUIRK_225_TOTALLBASWRITTEN) {
1778 static const SkSmartAttributeInfo a = {
1779 "total-lbas-written", SK_SMART_ATTRIBUTE_UNIT_MB, NULL
1783 /* %STRINGPOOLSTOP% */
1788 /* %STRINGPOOLSTART% */
1789 if (quirk & SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR) {
1790 static const SkSmartAttributeInfo a = {
1791 "timed-workload-media-wear", SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT, NULL
1795 /* %STRINGPOOLSTOP% */
1800 /* %STRINGPOOLSTART% */
1801 if (quirk & SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS) {
1802 static const SkSmartAttributeInfo a = {
1803 "timed-workload-host-reads", SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT, NULL
1807 /* %STRINGPOOLSTOP% */
1812 /* %STRINGPOOLSTART% */
1813 if (quirk & SK_SMART_QUIRK_228_WORKLOADTIMER) {
1814 static const SkSmartAttributeInfo a = {
1815 "workload-timer", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, NULL
1819 /* %STRINGPOOLSTOP% */
1824 /* %STRINGPOOLSTART% */
1825 if (quirk & SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE) {
1826 static const SkSmartAttributeInfo a = {
1827 "available-reserved-space", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL
1831 /* %STRINGPOOLSTOP% */
1835 /* %STRINGPOOLSTART% */
1836 if (quirk & SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR) {
1837 static const SkSmartAttributeInfo a = {
1838 "media-wearout-indicator", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL
1842 /* %STRINGPOOLSTOP% */
1848 /* These are the simple cases */
1849 if (attribute_info[id].name)
1850 return &attribute_info[id];
1855 int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **spd) {
1857 if (!d->smart_data_valid) {
1862 switch (d->smart_data[362]) {
1865 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER;
1870 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS;
1874 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS;
1879 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED;
1884 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED;
1889 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL;
1893 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN;
1897 d->smart_parsed_data.self_test_execution_percent_remaining = 10*(d->smart_data[363] & 0xF);
1898 d->smart_parsed_data.self_test_execution_status = (d->smart_data[363] >> 4) & 0xF;
1900 d->smart_parsed_data.total_offline_data_collection_seconds = (uint16_t) d->smart_data[364] | ((uint16_t) d->smart_data[365] << 8);
1902 d->smart_parsed_data.conveyance_test_available = disk_smart_is_conveyance_test_available(d);
1903 d->smart_parsed_data.short_and_extended_test_available = disk_smart_is_short_and_extended_test_available(d);
1904 d->smart_parsed_data.start_test_available = disk_smart_is_start_test_available(d);
1905 d->smart_parsed_data.abort_test_available = disk_smart_is_abort_test_available(d);
1907 d->smart_parsed_data.short_test_polling_minutes = d->smart_data[372];
1908 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]);
1909 d->smart_parsed_data.conveyance_test_polling_minutes = d->smart_data[374];
1911 *spd = &d->smart_parsed_data;
1916 static void find_threshold(SkDisk *d, SkSmartAttributeParsedData *a) {
1920 if (!d->smart_thresholds_valid)
1923 for (n = 0, p = d->smart_thresholds+2; n < 30; n++, p+=12)
1930 a->threshold = p[1];
1931 a->threshold_valid = p[1] != 0xFE;
1933 a->good_now_valid = FALSE;
1935 a->good_in_the_past_valid = FALSE;
1936 a->good_in_the_past = TRUE;
1938 /* Always-Fail and Always-Passing thresholds are not relevant
1939 * for our assessment. */
1940 if (p[1] >= 1 && p[1] <= 0xFD) {
1942 if (a->worst_value_valid) {
1943 a->good_in_the_past = a->good_in_the_past && (a->worst_value > a->threshold);
1944 a->good_in_the_past_valid = TRUE;
1947 if (a->current_value_valid) {
1948 a->good_now = a->good_now && (a->current_value > a->threshold);
1949 a->good_now_valid = TRUE;
1954 (a->good_now_valid && !a->good_now) ||
1955 (a->good_in_the_past_valid && !a->good_in_the_past);
1960 a->threshold_valid = FALSE;
1961 a->good_now_valid = FALSE;
1962 a->good_in_the_past_valid = FALSE;
1966 int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, void* userdata) {
1970 if (!d->smart_data_valid) {
1975 for (n = 0, p = d->smart_data + 2; n < 30; n++, p+=12) {
1976 SkSmartAttributeParsedData a;
1977 const SkSmartAttributeInfo *i;
1983 memset(&a, 0, sizeof(a));
1985 a.current_value = p[3];
1986 a.current_value_valid = p[3] >= 1 && p[3] <= 0xFD;
1987 a.worst_value = p[4];
1988 a.worst_value_valid = p[4] >= 1 && p[4] <= 0xFD;
1990 a.flags = ((uint16_t) p[2] << 8) | p[1];
1991 a.prefailure = !!(p[1] & 1);
1992 a.online = !!(p[1] & 2);
1994 memcpy(a.raw, p+5, 6);
1996 if ((i = lookup_attribute(d, p[0]))) {
1997 a.name = _P(i->name);
1998 a.pretty_unit = i->unit;
2000 if (asprintf(&an, "attribute-%u", a.id) < 0) {
2006 a.pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
2011 find_threshold(d, &a);
2016 cb(d, &a, userdata);
2023 static const char *yes_no(SkBool b) {
2024 return b ? "yes" : "no";
2027 const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit) {
2029 /* %STRINGPOOLSTART% */
2030 const char * const map[] = {
2031 [SK_SMART_ATTRIBUTE_UNIT_UNKNOWN] = NULL,
2032 [SK_SMART_ATTRIBUTE_UNIT_NONE] = "",
2033 [SK_SMART_ATTRIBUTE_UNIT_MSECONDS] = "ms",
2034 [SK_SMART_ATTRIBUTE_UNIT_SECTORS] = "sectors",
2035 [SK_SMART_ATTRIBUTE_UNIT_MKELVIN] = "mK",
2036 [SK_SMART_ATTRIBUTE_UNIT_PERCENT] = "%",
2037 [SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT] = "%",
2038 [SK_SMART_ATTRIBUTE_UNIT_MB] = "MB"
2040 /* %STRINGPOOLSTOP% */
2042 if (unit >= _SK_SMART_ATTRIBUTE_UNIT_MAX)
2045 return _P(map[unit]);
2048 struct attr_helper {
2053 static void temperature_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2055 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MKELVIN)
2058 if (!strcmp(a->name, "temperature-centi-celsius") ||
2059 !strcmp(a->name, "temperature-celsius") ||
2060 !strcmp(a->name, "temperature-celsius-2") ||
2061 !strcmp(a->name, "airflow-temperature-celsius")) {
2063 if (!ah->found || a->pretty_value > *ah->value)
2064 *ah->value = a->pretty_value;
2070 int sk_disk_smart_get_temperature(SkDisk *d, uint64_t *kelvin) {
2071 struct attr_helper ah;
2079 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) temperature_cb, &ah) < 0)
2090 static void power_on_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2092 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MSECONDS)
2095 if (!strcmp(a->name, "power-on-minutes") ||
2096 !strcmp(a->name, "power-on-seconds") ||
2097 !strcmp(a->name, "power-on-seconds-2") ||
2098 !strcmp(a->name, "power-on-half-minutes") ||
2099 !strcmp(a->name, "power-on-hours")) {
2101 if (!ah->found || a->pretty_value > *ah->value)
2102 *ah->value = a->pretty_value;
2108 int sk_disk_smart_get_power_on(SkDisk *d, uint64_t *mseconds) {
2109 struct attr_helper ah;
2115 ah.value = mseconds;
2117 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_on_cb, &ah) < 0)
2128 static void power_cycle_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2130 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_NONE)
2133 if (!strcmp(a->name, "power-cycle-count")) {
2135 if (!ah->found || a->pretty_value > *ah->value)
2136 *ah->value = a->pretty_value;
2142 int sk_disk_smart_get_power_cycle(SkDisk *d, uint64_t *count) {
2143 struct attr_helper ah;
2151 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_cycle_cb, &ah) < 0)
2162 static void reallocated_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2164 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
2167 if (!strcmp(a->name, "reallocated-sector-count")) {
2169 if (!ah->found || a->pretty_value > *ah->value)
2170 *ah->value = a->pretty_value;
2176 static void pending_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2178 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
2181 if (!strcmp(a->name, "current-pending-sector")) {
2183 if (!ah->found || a->pretty_value > *ah->value)
2184 *ah->value = a->pretty_value;
2190 int sk_disk_smart_get_bad(SkDisk *d, uint64_t *sectors) {
2191 struct attr_helper ah1, ah2;
2192 uint64_t sectors1, sectors2;
2198 ah1.value = §ors1;
2200 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) reallocated_cb, &ah1) < 0)
2204 ah2.value = §ors2;
2206 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) pending_cb, &ah2) < 0)
2209 if (!ah1.found && !ah2.found) {
2214 if (ah1.found && ah2.found)
2215 *sectors = sectors1 + sectors2;
2217 *sectors = sectors1;
2219 *sectors = sectors2;
2224 const char* sk_smart_overall_to_string(SkSmartOverall overall) {
2226 /* %STRINGPOOLSTART% */
2227 const char * const map[] = {
2228 [SK_SMART_OVERALL_GOOD] = "GOOD",
2229 [SK_SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST] = "BAD_ATTRIBUTE_IN_THE_PAST",
2230 [SK_SMART_OVERALL_BAD_SECTOR] = "BAD_SECTOR",
2231 [SK_SMART_OVERALL_BAD_ATTRIBUTE_NOW] = "BAD_ATTRIBUTE_NOW",
2232 [SK_SMART_OVERALL_BAD_SECTOR_MANY] = "BAD_SECTOR_MANY",
2233 [SK_SMART_OVERALL_BAD_STATUS] = "BAD_STATUS",
2235 /* %STRINGPOOLSTOP% */
2237 if (overall >= _SK_SMART_OVERALL_MAX)
2240 return _P(map[overall]);
2243 static void bad_attribute_now_cb(SkDisk *d, const SkSmartAttributeParsedData *a, SkBool *good) {
2244 if (a->prefailure && a->good_now_valid && !a->good_now)
2248 static void bad_attribute_in_the_past_cb(SkDisk *d, const SkSmartAttributeParsedData *a, SkBool *good) {
2249 if (a->prefailure && a->good_in_the_past_valid && !a->good_in_the_past)
2253 static uint64_t u64log2(uint64_t n) {
2268 int sk_disk_smart_get_overall(SkDisk *d, SkSmartOverall *overall) {
2270 uint64_t sectors, sector_threshold;
2275 /* First, check SMART self-assesment */
2276 if (sk_disk_smart_status(d, &good) < 0)
2280 *overall = SK_SMART_OVERALL_BAD_STATUS;
2284 /* Second, check if the number of bad sectors is greater than
2285 * a certain threshold */
2286 if (sk_disk_smart_get_bad(d, §ors) < 0) {
2287 if (errno != ENOENT)
2292 /* We use log2(n_sectors) as a threshold here. We had to pick
2293 * something, and this makes a bit of sense, or doesn't it? */
2294 sector_threshold = u64log2(d->size/512);
2296 if (sectors >= sector_threshold) {
2297 *overall = SK_SMART_OVERALL_BAD_SECTOR_MANY;
2302 /* Third, check if any of the SMART attributes is bad */
2304 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) bad_attribute_now_cb, &good) < 0)
2308 *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE_NOW;
2312 /* Fourth, check if there are any bad sectors at all */
2314 *overall = SK_SMART_OVERALL_BAD_SECTOR;
2318 /* Fifth, check if any of the SMART attributes ever was bad */
2320 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) bad_attribute_in_the_past_cb, &good) < 0)
2324 *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST;
2328 /* Sixth, there's really nothing to complain about, so give it a pass */
2329 *overall = SK_SMART_OVERALL_GOOD;
2333 static char* print_name(char *s, size_t len, uint8_t id, const char *k) {
2338 snprintf(s, len, "%u", id);
2345 static char *print_value(char *s, size_t len, uint64_t pretty_value, SkSmartAttributeUnit pretty_unit) {
2347 switch (pretty_unit) {
2348 case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
2350 if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU)
2351 snprintf(s, len, "%0.1f years", ((double) pretty_value)/(1000.0*60*60*24*365));
2352 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU)
2353 snprintf(s, len, "%0.1f months", ((double) pretty_value)/(1000.0*60*60*24*30));
2354 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU)
2355 snprintf(s, len, "%0.1f days", ((double) pretty_value)/(1000.0*60*60*24));
2356 else if (pretty_value >= 1000LLU*60LLU*60LLU)
2357 snprintf(s, len, "%0.1f h", ((double) pretty_value)/(1000.0*60*60));
2358 else if (pretty_value >= 1000LLU*60LLU)
2359 snprintf(s, len, "%0.1f min", ((double) pretty_value)/(1000.0*60));
2360 else if (pretty_value >= 1000LLU)
2361 snprintf(s, len, "%0.1f s", ((double) pretty_value)/(1000.0));
2363 snprintf(s, len, "%llu ms", (unsigned long long) pretty_value);
2367 case SK_SMART_ATTRIBUTE_UNIT_MKELVIN:
2368 snprintf(s, len, "%0.1f C", ((double) pretty_value - 273150) / 1000);
2371 case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
2372 snprintf(s, len, "%llu sectors", (unsigned long long) pretty_value);
2375 case SK_SMART_ATTRIBUTE_UNIT_PERCENT:
2376 snprintf(s, len, "%llu%%", (unsigned long long) pretty_value);
2379 case SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT:
2380 snprintf(s, len, "%0.3f%%", (double) pretty_value);
2383 case SK_SMART_ATTRIBUTE_UNIT_MB:
2384 if (pretty_value >= 1000000LLU)
2385 snprintf(s, len, "%0.3f TB", (double) pretty_value / 1000000LLU);
2386 else if (pretty_value >= 1000LLU)
2387 snprintf(s, len, "%0.3f GB", (double) pretty_value / 1000LLU);
2389 snprintf(s, len, "%llu MB", (unsigned long long) pretty_value);
2392 case SK_SMART_ATTRIBUTE_UNIT_NONE:
2393 snprintf(s, len, "%llu", (unsigned long long) pretty_value);
2396 case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
2397 snprintf(s, len, "n/a");
2400 case _SK_SMART_ATTRIBUTE_UNIT_MAX:
2409 #define HIGHLIGHT "\x1B[1m"
2410 #define ENDHIGHLIGHT "\x1B[0m"
2412 static void disk_dump_attributes(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) {
2415 char tt[32], tw[32], tc[32];
2418 snprintf(tt, sizeof(tt), "%3u", a->threshold);
2419 tt[sizeof(tt)-1] = 0;
2420 snprintf(tw, sizeof(tw), "%3u", a->worst_value);
2421 tw[sizeof(tw)-1] = 0;
2422 snprintf(tc, sizeof(tc), "%3u", a->current_value);
2423 tc[sizeof(tc)-1] = 0;
2425 highlight = a->warn && isatty(1);
2428 fprintf(stderr, HIGHLIGHT);
2430 printf("%3u %-27s %-3s %-3s %-3s %-11s 0x%02x%02x%02x%02x%02x%02x %-7s %-7s %-4s %-4s\n",
2432 print_name(name, sizeof(name), a->id, a->name),
2433 a->current_value_valid ? tc : "n/a",
2434 a->worst_value_valid ? tw : "n/a",
2435 a->threshold_valid ? tt : "n/a",
2436 print_value(pretty, sizeof(pretty), a->pretty_value, a->pretty_unit),
2437 a->raw[0], a->raw[1], a->raw[2], a->raw[3], a->raw[4], a->raw[5],
2438 a->prefailure ? "prefail" : "old-age",
2439 a->online ? "online" : "offline",
2440 a->good_now_valid ? yes_no(a->good_now) : "n/a",
2441 a->good_in_the_past_valid ? yes_no(a->good_in_the_past) : "n/a");
2444 fprintf(stderr, ENDHIGHLIGHT);
2447 int sk_disk_dump(SkDisk *d) {
2449 SkBool awake = FALSE;
2454 printf("Device: %s%s%s\n"
2456 d->name ? disk_type_to_prefix_string(d->type) : "",
2458 d->name ? d->name : "n/a",
2459 disk_type_to_human_string(d->type));
2461 ret = sk_disk_get_size(d, &size);
2463 printf("Size: %lu MiB\n", (unsigned long) (d->size/1024/1024));
2465 printf("Size: %s\n", strerror(errno));
2467 if (d->identify_valid) {
2468 const SkIdentifyParsedData *ipd;
2469 SkSmartQuirk quirk = 0;
2472 if ((ret = sk_disk_identify_parse(d, &ipd)) < 0)
2475 printf("Model: [%s]\n"
2478 "SMART Available: %s\n",
2482 yes_no(disk_smart_is_available(d)));
2484 if ((ret = lookup_quirks(ipd->model, ipd->firmware, &quirk)))
2489 for (i = 0; quirk_name[i]; i++)
2491 printf(" %s", _P(quirk_name[i]));
2496 ret = sk_disk_check_sleep_mode(d, &awake);
2497 printf("Awake: %s\n",
2498 ret >= 0 ? yes_no(awake) : strerror(errno));
2500 if (disk_smart_is_available(d)) {
2501 SkSmartOverall overall;
2502 const SkSmartParsedData *spd;
2505 uint64_t value, power_on;
2507 ret = sk_disk_smart_status(d, &good);
2508 printf("%sSMART Disk Health Good: %s%s\n",
2509 ret >= 0 && !good ? HIGHLIGHT : "",
2510 ret >= 0 ? yes_no(good) : strerror(errno),
2511 ret >= 0 && !good ? ENDHIGHLIGHT : "");
2512 if ((ret = sk_disk_smart_read_data(d)) < 0)
2515 if ((ret = sk_disk_smart_parse(d, &spd)) < 0)
2518 printf("Off-line Data Collection Status: [%s]\n"
2519 "Total Time To Complete Off-Line Data Collection: %u s\n"
2520 "Self-Test Execution Status: [%s]\n"
2521 "Percent Self-Test Remaining: %u%%\n"
2522 "Conveyance Self-Test Available: %s\n"
2523 "Short/Extended Self-Test Available: %s\n"
2524 "Start Self-Test Available: %s\n"
2525 "Abort Self-Test Available: %s\n"
2526 "Short Self-Test Polling Time: %u min\n"
2527 "Extended Self-Test Polling Time: %u min\n"
2528 "Conveyance Self-Test Polling Time: %u min\n",
2529 sk_smart_offline_data_collection_status_to_string(spd->offline_data_collection_status),
2530 spd->total_offline_data_collection_seconds,
2531 sk_smart_self_test_execution_status_to_string(spd->self_test_execution_status),
2532 spd->self_test_execution_percent_remaining,
2533 yes_no(spd->conveyance_test_available),
2534 yes_no(spd->short_and_extended_test_available),
2535 yes_no(spd->start_test_available),
2536 yes_no(spd->abort_test_available),
2537 spd->short_test_polling_minutes,
2538 spd->extended_test_polling_minutes,
2539 spd->conveyance_test_polling_minutes);
2541 if (sk_disk_smart_get_bad(d, &value) < 0)
2542 printf("Bad Sectors: %s\n", strerror(errno));
2544 printf("%sBad Sectors: %s%s\n",
2545 value > 0 ? HIGHLIGHT : "",
2546 print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_SECTORS),
2547 value > 0 ? ENDHIGHLIGHT : "");
2549 if (sk_disk_smart_get_power_on(d, &power_on) < 0) {
2550 printf("Powered On: %s\n", strerror(errno));
2553 printf("Powered On: %s\n", print_value(pretty, sizeof(pretty), power_on, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2555 if (sk_disk_smart_get_power_cycle(d, &value) < 0)
2556 printf("Power Cycles: %s\n", strerror(errno));
2558 printf("Power Cycles: %llu\n", (unsigned long long) value);
2560 if (value > 0 && power_on > 0)
2561 printf("Average Powered On Per Power Cycle: %s\n", print_value(pretty, sizeof(pretty), power_on/value, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2564 if (sk_disk_smart_get_temperature(d, &value) < 0)
2565 printf("Temperature: %s\n", strerror(errno));
2567 printf("Temperature: %s\n", print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_MKELVIN));
2569 printf("Attribute Parsing Verification: %s\n",
2570 d->attribute_verification_bad ? "Bad" : "Good");
2572 if (sk_disk_smart_get_overall(d, &overall) < 0)
2573 printf("Overall Status: %s\n", strerror(errno));
2575 printf("%sOverall Status: %s%s\n",
2576 overall != SK_SMART_OVERALL_GOOD ? HIGHLIGHT : "",
2577 sk_smart_overall_to_string(overall),
2578 overall != SK_SMART_OVERALL_GOOD ? ENDHIGHLIGHT : "");
2580 printf("%3s %-27s %5s %5s %5s %-11s %-14s %-7s %-7s %-4s %-4s\n",
2593 if ((ret = sk_disk_smart_parse_attributes(d, disk_dump_attributes, NULL)) < 0)
2596 printf("ATA SMART not supported.\n");
2601 int sk_disk_get_size(SkDisk *d, uint64_t *bytes) {
2605 if (d->size == (uint64_t) -1) {
2614 static int disk_find_type(SkDisk *d, dev_t devnum) {
2616 struct udev_device *dev = NULL, *usb;
2622 if (!(udev = udev_new())) {
2627 if (!(dev = udev_device_new_from_devnum(udev, 'b', devnum))) {
2632 if ((a = udev_device_get_property_value(dev, "ID_ATA_SMART_ACCESS"))) {
2635 for (u = 0; u < _SK_DISK_TYPE_MAX; u++) {
2638 if (!(t = disk_type_to_prefix_string(u)))
2641 if (!strcmp(a, t)) {
2648 d->type = SK_DISK_TYPE_NONE;
2653 if ((usb = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"))) {
2654 const char *product, *vendor;
2657 if (!(product = udev_device_get_sysattr_value(usb, "idProduct")) ||
2658 sscanf(product, "%04x", &pid) != 1) {
2663 if (!(vendor = udev_device_get_sysattr_value(usb, "idVendor")) ||
2664 sscanf(vendor, "%04x", &vid) != 1) {
2669 if ((vid == 0x0928 && pid == 0x0000))
2670 /* This Oxford Semiconductor bridge seems to
2671 * choke on SAT commands. Let's explicitly
2672 * black list it here.
2674 * http://bugs.freedesktop.org/show_bug.cgi?id=24951 */
2675 d->type = SK_DISK_TYPE_NONE;
2676 else if ((vid == 0x152d && pid == 0x2329) ||
2677 (vid == 0x152d && pid == 0x2338) ||
2678 (vid == 0x152d && pid == 0x2339))
2679 /* Some JMicron bridges seem to choke on SMART
2680 * commands, so let's explicitly black list
2683 * https://bugzilla.redhat.com/show_bug.cgi?id=515881
2685 * At least some of the JMicron bridges with
2686 * these vids/pids choke on the jmicron access
2687 * mode. To make sure we don't break things
2688 * for people we now disable this by
2690 d->type = SK_DISK_TYPE_NONE;
2691 else if ((vid == 0x152d && pid == 0x2336))
2692 /* This JMicron bridge seems to always work
2693 * with SMART commands send with the jmicron
2695 d->type = SK_DISK_TYPE_JMICRON;
2696 else if ((vid == 0x0c0b && pid == 0xb159) ||
2697 (vid == 0x04fc && pid == 0x0c25) ||
2698 (vid == 0x04fc && pid == 0x0c15))
2699 d->type = SK_DISK_TYPE_SUNPLUS;
2701 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_12;
2703 } else if (udev_device_get_parent_with_subsystem_devtype(dev, "ide", NULL))
2704 d->type = SK_DISK_TYPE_LINUX_IDE;
2705 else if (udev_device_get_parent_with_subsystem_devtype(dev, "scsi", NULL))
2706 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_16;
2708 d->type = SK_DISK_TYPE_AUTO;
2714 udev_device_unref(dev);
2722 static int init_smart(SkDisk *d) {
2723 /* We don't do the SMART initialization right-away, since some
2724 * drivers spin up when we do that */
2728 if (d->smart_initialized)
2731 d->smart_initialized = TRUE;
2733 /* Check if driver can do SMART, and enable if necessary */
2734 if (!disk_smart_is_available(d))
2737 if (!disk_smart_is_enabled(d)) {
2738 if ((ret = disk_smart_enable(d, TRUE)) < 0)
2741 if ((ret = disk_identify_device(d)) < 0)
2744 if (!disk_smart_is_enabled(d)) {
2751 disk_smart_read_thresholds(d);
2758 int sk_disk_open(const char *name, SkDisk **_d) {
2765 if (!(d = calloc(1, sizeof(SkDisk)))) {
2771 d->size = (uint64_t) -1;
2774 d->type = SK_DISK_TYPE_BLOB;
2778 d->type = SK_DISK_TYPE_AUTO;
2780 if (!(dn = disk_type_from_string(name, &d->type)))
2783 if (!(d->name = strdup(dn))) {
2788 if ((d->fd = open(d->name,
2789 O_RDONLY|O_NOCTTY|O_NONBLOCK
2799 if ((ret = fstat(d->fd, &st)) < 0)
2802 if (!S_ISBLK(st.st_mode)) {
2808 /* So, it's a block device. Let's make sure the ioctls work */
2809 if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0)
2812 if (d->size <= 0 || d->size == (uint64_t) -1) {
2818 /* OK, it's a real block device with a size. Now let's find the suitable API */
2819 if (d->type == SK_DISK_TYPE_AUTO)
2820 if ((ret = disk_find_type(d, st.st_rdev)) < 0)
2823 if (d->type == SK_DISK_TYPE_AUTO) {
2824 /* We have no clue, so let's autotest for a working API */
2825 for (d->type = 0; d->type < _SK_DISK_TYPE_TEST_MAX; d->type++)
2826 if (disk_identify_device(d) >= 0)
2828 if (d->type >= _SK_DISK_TYPE_TEST_MAX)
2829 d->type = SK_DISK_TYPE_NONE;
2831 disk_identify_device(d);
2846 void sk_disk_free(SkDisk *d) {
2857 int sk_disk_get_blob(SkDisk *d, const void **blob, size_t *rsize) {
2859 SkBool good, have_good = FALSE;
2867 (d->identify_valid ? 8 + sizeof(d->identify) : 0) +
2868 (d->smart_data_valid ? 8 + sizeof(d->smart_data) : 0) +
2869 (d->smart_thresholds_valid ? 8 + sizeof(d->smart_thresholds) : 0);
2871 if (sk_disk_smart_status(d, &good) >= 0) {
2882 if (!(d->blob = malloc(size))) {
2889 /* These memory accesses are only OK as long as all our
2890 * objects are sensibly aligned, which they are... */
2892 if (d->identify_valid) {
2893 p[0] = SK_BLOB_TAG_IDENTIFY;
2894 p[1] = htonl(sizeof(d->identify));
2897 memcpy(p, d->identify, sizeof(d->identify));
2898 p = (uint32_t*) ((uint8_t*) p + sizeof(d->identify));
2902 p[0] = SK_BLOB_TAG_SMART_STATUS;
2904 p[2] = htonl(!!good);
2908 if (d->smart_data_valid) {
2909 p[0] = SK_BLOB_TAG_SMART_DATA;
2910 p[1] = htonl(sizeof(d->smart_data));
2913 memcpy(p, d->smart_data, sizeof(d->smart_data));
2914 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_data));
2917 if (d->smart_thresholds_valid) {
2918 p[0] = SK_BLOB_TAG_SMART_THRESHOLDS;
2919 p[1] = htonl(sizeof(d->smart_thresholds));
2922 memcpy(p, d->smart_thresholds, sizeof(d->smart_thresholds));
2923 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_thresholds));
2926 assert((size_t) ((uint8_t*) p - (uint8_t*) d->blob) == size);
2934 int sk_disk_set_blob(SkDisk *d, const void *blob, size_t size) {
2937 SkBool idv = FALSE, sdv = FALSE, stv = FALSE, bssv = FALSE;
2942 if (d->type != SK_DISK_TYPE_BLOB) {
2952 /* First run, verify if everything makes sense */
2956 uint32_t tag, tsize;
2964 memcpy(&tsize, p+1, 4);
2968 if (left < ntohl(tsize)) {
2975 case SK_BLOB_TAG_IDENTIFY:
2976 if (ntohl(tsize) != sizeof(d->identify) || idv) {
2983 case SK_BLOB_TAG_SMART_STATUS:
2984 if (ntohl(tsize) != 4 || bssv) {
2991 case SK_BLOB_TAG_SMART_DATA:
2992 if (ntohl(tsize) != sizeof(d->smart_data) || sdv) {
2999 case SK_BLOB_TAG_SMART_THRESHOLDS:
3000 if (ntohl(tsize) != sizeof(d->smart_thresholds) || stv) {
3008 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
3009 left -= ntohl(tsize);
3017 d->identify_valid = idv;
3018 d->smart_data_valid = sdv;
3019 d->smart_thresholds_valid = stv;
3020 d->blob_smart_status_valid = bssv;
3022 /* Second run, actually copy things in */
3026 uint32_t tag, tsize;
3030 memcpy(&tsize, p+1, 4);
3034 assert(left >= ntohl(tsize));
3038 case SK_BLOB_TAG_IDENTIFY:
3039 assert(ntohl(tsize) == sizeof(d->identify));
3040 memcpy(d->identify, p, sizeof(d->identify));
3043 case SK_BLOB_TAG_SMART_STATUS: {
3045 assert(ntohl(tsize) == 4);
3047 d->blob_smart_status = !!ok;
3051 case SK_BLOB_TAG_SMART_DATA:
3052 assert(ntohl(tsize) == sizeof(d->smart_data));
3053 memcpy(d->smart_data, p, sizeof(d->smart_data));
3056 case SK_BLOB_TAG_SMART_THRESHOLDS:
3057 assert(ntohl(tsize) == sizeof(d->smart_thresholds));
3058 memcpy(d->smart_thresholds, p, sizeof(d->smart_thresholds));
3062 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
3063 left -= ntohl(tsize);