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;
133
SkBool reallocated_sector_count_bad:1;
134
SkBool current_pending_sector_bad:1;
140
typedef enum SkAtaCommand {
141
SK_ATA_COMMAND_IDENTIFY_DEVICE = 0xEC,
142
SK_ATA_COMMAND_IDENTIFY_PACKET_DEVICE = 0xA1,
143
SK_ATA_COMMAND_SMART = 0xB0,
144
SK_ATA_COMMAND_CHECK_POWER_MODE = 0xE5
147
/* ATA SMART subcommands (ATA8 7.52.1) */
148
typedef enum SkSmartCommand {
149
SK_SMART_COMMAND_READ_DATA = 0xD0,
150
SK_SMART_COMMAND_READ_THRESHOLDS = 0xD1,
151
SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE = 0xD4,
152
SK_SMART_COMMAND_ENABLE_OPERATIONS = 0xD8,
153
SK_SMART_COMMAND_DISABLE_OPERATIONS = 0xD9,
154
SK_SMART_COMMAND_RETURN_STATUS = 0xDA
157
/* Hmm, if the data we parse is out of a certain range just consider it misparsed */
158
#define SK_MKELVIN_VALID_MIN ((uint64_t) ((-15LL*1000LL) + 273150LL))
159
#define SK_MKELVIN_VALID_MAX ((uint64_t) ((100LL*1000LL) + 273150LL))
161
#define SK_MSECOND_VALID_MIN 1ULL
162
#define SK_MSECOND_VALID_SHORT_MAX (60ULL * 60ULL * 1000ULL)
163
#define SK_MSECOND_VALID_LONG_MAX (30ULL * 365ULL * 24ULL * 60ULL * 60ULL * 1000ULL)
165
static int init_smart(SkDisk *d);
167
static const char *disk_type_to_human_string(SkDiskType type) {
169
/* %STRINGPOOLSTART% */
170
static const char* const map[_SK_DISK_TYPE_MAX] = {
171
[SK_DISK_TYPE_ATA_PASSTHROUGH_16] = "16 Byte SCSI ATA SAT Passthru",
172
[SK_DISK_TYPE_ATA_PASSTHROUGH_12] = "12 Byte SCSI ATA SAT Passthru",
173
[SK_DISK_TYPE_LINUX_IDE] = "Native Linux IDE",
174
[SK_DISK_TYPE_SUNPLUS] = "Sunplus SCSI ATA Passthru",
175
[SK_DISK_TYPE_JMICRON] = "JMicron SCSI ATA Passthru",
176
[SK_DISK_TYPE_BLOB] = "Blob",
177
[SK_DISK_TYPE_AUTO] = "Automatic",
178
[SK_DISK_TYPE_NONE] = "None"
180
/* %STRINGPOOLSTOP% */
182
if (type >= _SK_DISK_TYPE_MAX)
185
return _P(map[type]);
188
static const char *disk_type_to_prefix_string(SkDiskType type) {
190
/* %STRINGPOOLSTART% */
191
static const char* const map[_SK_DISK_TYPE_MAX] = {
192
[SK_DISK_TYPE_ATA_PASSTHROUGH_16] = "sat16",
193
[SK_DISK_TYPE_ATA_PASSTHROUGH_12] = "sat12",
194
[SK_DISK_TYPE_LINUX_IDE] = "linux-ide",
195
[SK_DISK_TYPE_SUNPLUS] = "sunplus",
196
[SK_DISK_TYPE_JMICRON] = "jmicron",
197
[SK_DISK_TYPE_NONE] = "none",
198
[SK_DISK_TYPE_AUTO] = "auto",
200
/* %STRINGPOOLSTOP% */
202
if (type >= _SK_DISK_TYPE_MAX)
205
return _P(map[type]);
208
static const char *disk_type_from_string(const char *s, SkDiskType *type) {
214
for (u = 0; u < _SK_DISK_TYPE_MAX; u++) {
218
if (!(t = disk_type_to_prefix_string(u)))
223
if (strncmp(s, t, l))
237
static SkBool disk_smart_is_available(SkDisk *d) {
238
return d->identify_valid && !!(d->identify[164] & 1);
241
static SkBool disk_smart_is_enabled(SkDisk *d) {
242
return d->identify_valid && !!(d->identify[170] & 1);
245
static SkBool disk_smart_is_conveyance_test_available(SkDisk *d) {
246
assert(d->smart_data_valid);
248
return !!(d->smart_data[367] & 32);
250
static SkBool disk_smart_is_short_and_extended_test_available(SkDisk *d) {
251
assert(d->smart_data_valid);
253
return !!(d->smart_data[367] & 16);
256
static SkBool disk_smart_is_start_test_available(SkDisk *d) {
257
assert(d->smart_data_valid);
259
return !!(d->smart_data[367] & 1);
262
static SkBool disk_smart_is_abort_test_available(SkDisk *d) {
263
assert(d->smart_data_valid);
265
return !!(d->smart_data[367] & 41);
268
static int disk_linux_ide_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
269
uint8_t *bytes = cmd_data;
272
assert(d->type == SK_DISK_TYPE_LINUX_IDE);
276
case SK_DIRECTION_OUT:
278
/* We could use HDIO_DRIVE_TASKFILE here, but
279
* that's a deprecated ioctl(), hence we don't
280
* do it. And we don't need writing anyway. */
285
case SK_DIRECTION_IN: {
288
/* We have HDIO_DRIVE_CMD which can only read, but not write,
289
* and cannot do LBA. We use it for all read commands. */
291
ioctl_data = alloca(4 + *len);
292
memset(ioctl_data, 0, 4 + *len);
294
ioctl_data[0] = (uint8_t) command; /* COMMAND */
295
ioctl_data[1] = ioctl_data[0] == WIN_SMART ? bytes[9] : bytes[3]; /* SECTOR/NSECTOR */
296
ioctl_data[2] = bytes[1]; /* FEATURE */
297
ioctl_data[3] = bytes[3]; /* NSECTOR */
299
if ((ret = ioctl(d->fd, HDIO_DRIVE_CMD, ioctl_data)) < 0)
302
memset(bytes, 0, 12);
303
bytes[11] = ioctl_data[0];
304
bytes[1] = ioctl_data[1];
305
bytes[3] = ioctl_data[2];
307
memcpy(data, ioctl_data+4, *len);
312
case SK_DIRECTION_NONE: {
313
uint8_t ioctl_data[7];
315
/* We have HDIO_DRIVE_TASK which can neither read nor
316
* write, but can do LBA. We use it for all commands that
317
* do neither read nor write */
319
memset(ioctl_data, 0, sizeof(ioctl_data));
321
ioctl_data[0] = (uint8_t) command; /* COMMAND */
322
ioctl_data[1] = bytes[1]; /* FEATURE */
323
ioctl_data[2] = bytes[3]; /* NSECTOR */
325
ioctl_data[3] = bytes[9]; /* LBA LOW */
326
ioctl_data[4] = bytes[8]; /* LBA MID */
327
ioctl_data[5] = bytes[7]; /* LBA HIGH */
328
ioctl_data[6] = bytes[10]; /* SELECT */
330
if ((ret = ioctl(d->fd, HDIO_DRIVE_TASK, ioctl_data)))
333
memset(bytes, 0, 12);
334
bytes[11] = ioctl_data[0];
335
bytes[1] = ioctl_data[1];
336
bytes[3] = ioctl_data[2];
338
bytes[9] = ioctl_data[3];
339
bytes[8] = ioctl_data[4];
340
bytes[7] = ioctl_data[5];
342
bytes[10] = ioctl_data[6];
353
/* Sends a SCSI command block */
354
static int sg_io(int fd, int direction,
355
const void *cdb, size_t cdb_len,
356
void *data, size_t data_len,
357
void *sense, size_t sense_len) {
359
struct sg_io_hdr io_hdr;
361
memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
363
io_hdr.interface_id = 'S';
364
io_hdr.cmdp = (unsigned char*) cdb;
365
io_hdr.cmd_len = cdb_len;
366
io_hdr.dxferp = data;
367
io_hdr.dxfer_len = data_len;
369
io_hdr.mx_sb_len = sense_len;
370
io_hdr.dxfer_direction = direction;
371
io_hdr.timeout = SK_TIMEOUT;
373
return ioctl(fd, SG_IO, &io_hdr);
376
static int disk_passthrough_16_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
377
uint8_t *bytes = cmd_data;
380
uint8_t *desc = sense+8;
383
static const int direction_map[] = {
384
[SK_DIRECTION_NONE] = SG_DXFER_NONE,
385
[SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
386
[SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
389
assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_16);
391
/* ATA Pass-Through 16 byte command, as described in "T10 04-262r8
392
* ATA Command Pass-Through":
393
* http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */
395
memset(cdb, 0, sizeof(cdb));
397
cdb[0] = 0x85; /* OPERATION CODE: 16 byte pass through */
399
if (direction == SK_DIRECTION_NONE) {
400
cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */
401
cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */
403
} else if (direction == SK_DIRECTION_IN) {
404
cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
405
cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
407
} else if (direction == SK_DIRECTION_OUT) {
408
cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */
409
cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */
412
cdb[3] = bytes[0]; /* FEATURES */
415
cdb[5] = bytes[2]; /* SECTORS */
418
cdb[8] = bytes[9]; /* LBA LOW */
419
cdb[10] = bytes[8]; /* LBA MID */
420
cdb[12] = bytes[7]; /* LBA HIGH */
422
cdb[13] = bytes[10] & 0x4F; /* SELECT */
423
cdb[14] = (uint8_t) command;
425
memset(sense, 0, sizeof(sense));
427
if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
430
if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) {
435
memset(bytes, 0, 12);
443
bytes[10] = desc[12];
444
bytes[11] = desc[13];
449
static int disk_passthrough_12_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
450
uint8_t *bytes = cmd_data;
453
uint8_t *desc = sense+8;
456
static const int direction_map[] = {
457
[SK_DIRECTION_NONE] = SG_DXFER_NONE,
458
[SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
459
[SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
462
assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12);
464
/* ATA Pass-Through 12 byte command, as described in "T10 04-262r8
465
* ATA Command Pass-Through":
466
* http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */
468
memset(cdb, 0, sizeof(cdb));
470
cdb[0] = 0xa1; /* OPERATION CODE: 12 byte pass through */
472
if (direction == SK_DIRECTION_NONE) {
473
cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */
474
cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */
476
} else if (direction == SK_DIRECTION_IN) {
477
cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
478
cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
480
} else if (direction == SK_DIRECTION_OUT) {
481
cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */
482
cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */
485
cdb[3] = bytes[1]; /* FEATURES */
486
cdb[4] = bytes[3]; /* SECTORS */
488
cdb[5] = bytes[9]; /* LBA LOW */
489
cdb[6] = bytes[8]; /* LBA MID */
490
cdb[7] = bytes[7]; /* LBA HIGH */
492
cdb[8] = bytes[10] & 0x4F; /* SELECT */
493
cdb[9] = (uint8_t) command;
495
memset(sense, 0, sizeof(sense));
497
if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
500
if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) {
505
memset(bytes, 0, 12);
507
bytes[1] = desc[3]; /* FEATURES */
508
bytes[2] = desc[4]; /* STATUS */
509
bytes[3] = desc[5]; /* SECTORS */
510
bytes[9] = desc[7]; /* LBA LOW */
511
bytes[8] = desc[9]; /* LBA MID */
512
bytes[7] = desc[11]; /* LBA HIGH */
513
bytes[10] = desc[12]; /* SELECT */
514
bytes[11] = desc[13]; /* ERROR */
519
static int disk_sunplus_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
520
uint8_t *bytes = cmd_data;
522
uint8_t sense[32], buf[8];
524
static const int direction_map[] = {
525
[SK_DIRECTION_NONE] = SG_DXFER_NONE,
526
[SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
527
[SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
530
assert(d->type == SK_DISK_TYPE_SUNPLUS);
532
/* SunplusIT specific SCSI ATA pass-thru. Inspired by smartmonutils' support for these bridges */
534
memset(cdb, 0, sizeof(cdb));
536
cdb[0] = 0xF8; /* OPERATION CODE: Sunplus specific */
537
cdb[1] = 0x00; /* Subcommand: Pass-thru */
540
if (direction == SK_DIRECTION_NONE)
541
cdb[3] = 0x00; /* protocol */
542
else if (direction == SK_DIRECTION_IN)
543
cdb[3] = 0x10; /* protocol */
544
else if (direction == SK_DIRECTION_OUT)
545
cdb[3] = 0x11; /* protocol */
547
cdb[4] = bytes[3]; /* size? */
548
cdb[5] = bytes[1]; /* FEATURES */
549
cdb[6] = bytes[3]; /* SECTORS */
550
cdb[7] = bytes[9]; /* LBA LOW */
551
cdb[8] = bytes[8]; /* LBA MID */
552
cdb[9] = bytes[7]; /* LBA HIGH */
553
cdb[10] = bytes[10] | 0xA0; /* SELECT */
554
cdb[11] = (uint8_t) command;
556
memset(sense, 0, sizeof(sense));
559
if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
562
memset(cdb, 0, sizeof(cdb));
568
memset(buf, 0, sizeof(buf));
570
/* Ask for response */
571
if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), buf, sizeof(buf), sense, sizeof(sense))) < 0)
574
memset(bytes, 0, 12);
576
bytes[2] = buf[1]; /* ERROR */
577
bytes[3] = buf[2]; /* SECTORS */
578
bytes[9] = buf[3]; /* LBA LOW */
579
bytes[8] = buf[4]; /* LBA MID */
580
bytes[7] = buf[5]; /* LBA HIGH */
581
bytes[10] = buf[6]; /* SELECT */
582
bytes[11] = buf[7]; /* STATUS */
587
static int disk_jmicron_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* _data, size_t *_len) {
588
uint8_t *bytes = cmd_data;
593
SkBool is_smart_status = FALSE;
595
size_t len = _len ? *_len : 0;
596
uint8_t smart_status = 0;
598
static const int direction_map[] = {
599
[SK_DIRECTION_NONE] = SG_DXFER_NONE,
600
[SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
601
[SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
604
assert(d->type == SK_DISK_TYPE_JMICRON);
606
/* JMicron specific SCSI ATA pass-thru. Inspired by smartmonutils' support for these bridges */
608
memset(cdb, 0, sizeof(cdb));
610
cdb[0] = 0xdf; /* operation code */
613
cdb[3] = 0x00; /* size HI */
614
cdb[4] = sizeof(port); /* size LO */
616
cdb[6] = 0x72; /* register address HI */
617
cdb[7] = 0x0f; /* register address LO */
623
memset(sense, 0, sizeof(sense));
625
if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), &port, sizeof(port), sense, sizeof(sense))) < 0)
628
/* Port & 0x04 is port #0, Port & 0x40 is port #1 */
632
cdb[0] = 0xdf; /* OPERATION CODE: 12 byte pass through */
634
if (command == SK_ATA_COMMAND_SMART && bytes[1] == SK_SMART_COMMAND_RETURN_STATUS) {
635
/* We need to rewrite the SMART status request */
636
is_smart_status = TRUE;
637
direction = SK_DIRECTION_IN;
638
data = &smart_status;
639
len = sizeof(smart_status);
641
} else if (direction == SK_DIRECTION_NONE)
643
else if (direction == SK_DIRECTION_IN)
645
else if (direction == SK_DIRECTION_OUT)
650
cdb[3] = (uint8_t) (len >> 8);
651
cdb[4] = (uint8_t) (len & 0xFF);
653
cdb[5] = bytes[1]; /* FEATURES */
654
cdb[6] = bytes[3]; /* SECTORS */
656
cdb[7] = bytes[9]; /* LBA LOW */
657
cdb[8] = bytes[8]; /* LBA MID */
658
cdb[9] = bytes[7]; /* LBA HIGH */
660
cdb[10] = bytes[10] | ((port & 0x04) ? 0xA0 : 0xB0); /* SELECT */
661
cdb[11] = (uint8_t) command;
663
memset(sense, 0, sizeof(sense));
665
if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len, sense, sizeof(sense))) < 0)
668
memset(bytes, 0, 12);
670
if (is_smart_status) {
671
if (smart_status == 0x01 || smart_status == 0xc2) {
672
bytes[7] = 0xc2; /* LBA HIGH */
673
bytes[8] = 0x4f; /* LBA MID */
674
} else if (smart_status == 0x00 || smart_status == 0x2c) {
675
bytes[7] = 0x2c; /* LBA HIGH */
676
bytes[8] = 0xf4; /* LBA MID */
682
cdb[0] = 0xdf; /* operation code */
685
cdb[3] = 0x00; /* size HI */
686
cdb[4] = sizeof(regbuf); /* size LO */
688
cdb[6] = (port & 0x04) ? 0x80 : 0x90; /* register address HI */
689
cdb[7] = 0x00; /* register address LO */
695
if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), regbuf, sizeof(regbuf), sense, sizeof(sense))) < 0)
698
bytes[2] = regbuf[14]; /* STATUS */
699
bytes[3] = regbuf[0]; /* SECTORS */
700
bytes[9] = regbuf[6]; /* LBA LOW */
701
bytes[8] = regbuf[4]; /* LBA MID */
702
bytes[7] = regbuf[10]; /* LBA HIGH */
703
bytes[10] = regbuf[9]; /* SELECT */
704
bytes[11] = regbuf[13]; /* ERROR */
710
static int disk_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
712
static int (* const disk_command_table[_SK_DISK_TYPE_MAX]) (SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) = {
713
[SK_DISK_TYPE_LINUX_IDE] = disk_linux_ide_command,
714
[SK_DISK_TYPE_ATA_PASSTHROUGH_12] = disk_passthrough_12_command,
715
[SK_DISK_TYPE_ATA_PASSTHROUGH_16] = disk_passthrough_16_command,
716
[SK_DISK_TYPE_SUNPLUS] = disk_sunplus_command,
717
[SK_DISK_TYPE_JMICRON] = disk_jmicron_command,
718
[SK_DISK_TYPE_BLOB] = NULL,
719
[SK_DISK_TYPE_AUTO] = NULL,
720
[SK_DISK_TYPE_NONE] = NULL
724
assert(d->type <= _SK_DISK_TYPE_MAX);
725
assert(direction <= _SK_DIRECTION_MAX);
727
assert(direction == SK_DIRECTION_NONE || (data && len && *len > 0));
728
assert(direction != SK_DIRECTION_NONE || (!data && !len));
730
if (!disk_command_table[d->type]) {
735
return disk_command_table[d->type](d, command, direction, cmd_data, data, len);
738
static int disk_identify_device(SkDisk *d) {
744
if (d->type == SK_DISK_TYPE_BLOB)
747
memset(d->identify, 0, len);
748
memset(cmd, 0, sizeof(cmd));
752
if ((ret = disk_command(d, SK_ATA_COMMAND_IDENTIFY_DEVICE, SK_DIRECTION_IN, cmd, d->identify, &len)) < 0)
760
/* Check if IDENTIFY data is all NULs */
761
for (p = d->identify; p < (const uint8_t*) d->identify+len; p++)
772
d->identify_valid = TRUE;
777
int sk_disk_check_sleep_mode(SkDisk *d, SkBool *awake) {
782
if (!d->identify_valid) {
787
if (d->type == SK_DISK_TYPE_BLOB) {
792
memset(cmd, 0, sizeof(cmd));
794
if ((ret = disk_command(d, SK_ATA_COMMAND_CHECK_POWER_MODE, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
797
if (cmd[0] != 0 || (ntohs(cmd[5]) & 1) != 0) {
802
status = ntohs(cmd[1]) & 0xFF;
803
*awake = status == 0xFF || status == 0x80; /* idle and active/idle is considered awake */
808
static int disk_smart_enable(SkDisk *d, SkBool b) {
811
if (!disk_smart_is_available(d)) {
816
if (d->type == SK_DISK_TYPE_BLOB) {
821
memset(cmd, 0, sizeof(cmd));
823
cmd[0] = htons(b ? SK_SMART_COMMAND_ENABLE_OPERATIONS : SK_SMART_COMMAND_DISABLE_OPERATIONS);
824
cmd[2] = htons(0x0000U);
825
cmd[3] = htons(0x00C2U);
826
cmd[4] = htons(0x4F00U);
828
return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0);
831
int sk_disk_smart_read_data(SkDisk *d) {
836
if (init_smart(d) < 0)
839
if (!disk_smart_is_available(d)) {
844
if (d->type == SK_DISK_TYPE_BLOB)
847
memset(cmd, 0, sizeof(cmd));
849
cmd[0] = htons(SK_SMART_COMMAND_READ_DATA);
851
cmd[2] = htons(0x0000U);
852
cmd[3] = htons(0x00C2U);
853
cmd[4] = htons(0x4F00U);
855
if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_data, &len)) < 0)
858
d->smart_data_valid = TRUE;
863
static int disk_smart_read_thresholds(SkDisk *d) {
868
if (!disk_smart_is_available(d)) {
873
if (d->type == SK_DISK_TYPE_BLOB)
876
memset(cmd, 0, sizeof(cmd));
878
cmd[0] = htons(SK_SMART_COMMAND_READ_THRESHOLDS);
880
cmd[2] = htons(0x0000U);
881
cmd[3] = htons(0x00C2U);
882
cmd[4] = htons(0x4F00U);
884
if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_thresholds, &len)) < 0)
887
d->smart_thresholds_valid = TRUE;
892
int sk_disk_smart_status(SkDisk *d, SkBool *good) {
896
if (init_smart(d) < 0)
899
if (!disk_smart_is_available(d)) {
904
if (d->type == SK_DISK_TYPE_BLOB) {
906
if (d->blob_smart_status_valid) {
907
*good = d->blob_smart_status;
915
memset(cmd, 0, sizeof(cmd));
917
cmd[0] = htons(SK_SMART_COMMAND_RETURN_STATUS);
918
cmd[1] = htons(0x0000U);
919
cmd[3] = htons(0x00C2U);
920
cmd[4] = htons(0x4F00U);
922
if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
925
/* SAT/USB bridges truncate packets, so we only check for 4F,
926
* not for 2C on those */
927
if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x00C2U)) &&
928
cmd[4] == htons(0x4F00U))
930
else if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x002CU)) &&
931
cmd[4] == htons(0xF400U))
941
int sk_disk_smart_self_test(SkDisk *d, SkSmartSelfTest test) {
945
if (init_smart(d) < 0)
948
if (!disk_smart_is_available(d)) {
953
if (d->type == SK_DISK_TYPE_BLOB) {
958
if (!d->smart_data_valid)
959
if ((ret = sk_disk_smart_read_data(d)) < 0)
962
assert(d->smart_data_valid);
964
if (test != SK_SMART_SELF_TEST_SHORT &&
965
test != SK_SMART_SELF_TEST_EXTENDED &&
966
test != SK_SMART_SELF_TEST_CONVEYANCE &&
967
test != SK_SMART_SELF_TEST_ABORT) {
972
if (!disk_smart_is_start_test_available(d)
973
|| (test == SK_SMART_SELF_TEST_ABORT && !disk_smart_is_abort_test_available(d))
974
|| ((test == SK_SMART_SELF_TEST_SHORT || test == SK_SMART_SELF_TEST_EXTENDED) && !disk_smart_is_short_and_extended_test_available(d))
975
|| (test == SK_SMART_SELF_TEST_CONVEYANCE && !disk_smart_is_conveyance_test_available(d))) {
980
if (test == SK_SMART_SELF_TEST_ABORT &&
981
!disk_smart_is_abort_test_available(d)) {
986
memset(cmd, 0, sizeof(cmd));
988
cmd[0] = htons(SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE);
989
cmd[2] = htons(0x0000U);
990
cmd[3] = htons(0x00C2U);
991
cmd[4] = htons(0x4F00U | (uint16_t) test);
993
return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, NULL);
996
static void swap_strings(char *s, size_t len) {
997
assert((len & 1) == 0);
999
for (; len > 0; s += 2, len -= 2) {
1007
static void clean_strings(char *s) {
1010
for (e = s; *e; e++)
1011
if (*e < ' ' || *e >= 127)
1015
static void drop_spaces(char *s) {
1017
SkBool prev_space = FALSE;
1019
s += strspn(s, " ");
1040
static void read_string(char *d, uint8_t *s, size_t len) {
1043
swap_strings(d, len);
1048
int sk_disk_identify_parse(SkDisk *d, const SkIdentifyParsedData **ipd) {
1052
if (!d->identify_valid) {
1057
read_string(d->identify_parsed_data.serial, d->identify+20, 20);
1058
read_string(d->identify_parsed_data.firmware, d->identify+46, 8);
1059
read_string(d->identify_parsed_data.model, d->identify+54, 40);
1061
*ipd = &d->identify_parsed_data;
1066
int sk_disk_smart_is_available(SkDisk *d, SkBool *b) {
1070
if (!d->identify_valid) {
1075
*b = disk_smart_is_available(d);
1079
int sk_disk_identify_is_available(SkDisk *d, SkBool *b) {
1083
*b = d->identify_valid;
1087
const char *sk_smart_offline_data_collection_status_to_string(SkSmartOfflineDataCollectionStatus status) {
1089
/* %STRINGPOOLSTART% */
1090
static const char* const map[] = {
1091
[SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER] = "Off-line data collection activity was never started.",
1092
[SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS] = "Off-line data collection activity was completed without error.",
1093
[SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS] = "Off-line activity in progress.",
1094
[SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED] = "Off-line data collection activity was suspended by an interrupting command from host.",
1095
[SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED] = "Off-line data collection activity was aborted by an interrupting command from host.",
1096
[SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL] = "Off-line data collection activity was aborted by the device with a fatal error.",
1097
[SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN] = "Unknown status"
1099
/* %STRINGPOOLSTOP% */
1101
if (status >= _SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_MAX)
1104
return _P(map[status]);
1107
const char *sk_smart_self_test_execution_status_to_string(SkSmartSelfTestExecutionStatus status) {
1109
/* %STRINGPOOLSTART% */
1110
static const char* const map[] = {
1111
[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.",
1112
[SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED] = "The self-test routine was aborted by the host.",
1113
[SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED] = "The self-test routine was interrupted by the host with a hardware or software reset.",
1114
[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.",
1115
[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.",
1116
[SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL] = "The previous self-test completed having the electrical element of the test failed.",
1117
[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.",
1118
[SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ] = "The previous self-test completed having the read element of the test failed.",
1119
[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.",
1120
[SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS] = "Self-test routine in progress"
1122
/* %STRINGPOOLSTOP% */
1124
if (status >= _SK_SMART_SELF_TEST_EXECUTION_STATUS_MAX)
1127
return _P(map[status]);
1130
const char* sk_smart_self_test_to_string(SkSmartSelfTest test) {
1133
case SK_SMART_SELF_TEST_SHORT:
1135
case SK_SMART_SELF_TEST_EXTENDED:
1137
case SK_SMART_SELF_TEST_CONVEYANCE:
1138
return "conveyance";
1139
case SK_SMART_SELF_TEST_ABORT:
1146
SkBool sk_smart_self_test_available(const SkSmartParsedData *d, SkSmartSelfTest test) {
1149
if (!d->start_test_available)
1153
case SK_SMART_SELF_TEST_SHORT:
1154
case SK_SMART_SELF_TEST_EXTENDED:
1155
return d->short_and_extended_test_available;
1156
case SK_SMART_SELF_TEST_CONVEYANCE:
1157
return d->conveyance_test_available;
1158
case SK_SMART_SELF_TEST_ABORT:
1159
return d->abort_test_available;
1165
unsigned sk_smart_self_test_polling_minutes(const SkSmartParsedData *d, SkSmartSelfTest test) {
1168
if (!sk_smart_self_test_available(d, test))
1172
case SK_SMART_SELF_TEST_SHORT:
1173
return d->short_test_polling_minutes;
1174
case SK_SMART_SELF_TEST_EXTENDED:
1175
return d->extended_test_polling_minutes;
1176
case SK_SMART_SELF_TEST_CONVEYANCE:
1177
return d->conveyance_test_polling_minutes;
1183
static void make_pretty(SkSmartAttributeParsedData *a) {
1184
uint64_t fourtyeight;
1189
if (a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_UNKNOWN)
1193
((uint64_t) a->raw[0]) |
1194
(((uint64_t) a->raw[1]) << 8) |
1195
(((uint64_t) a->raw[2]) << 16) |
1196
(((uint64_t) a->raw[3]) << 24) |
1197
(((uint64_t) a->raw[4]) << 32) |
1198
(((uint64_t) a->raw[5]) << 40);
1200
if (!strcmp(a->name, "spin-up-time"))
1201
a->pretty_value = fourtyeight & 0xFFFF;
1202
else if (!strcmp(a->name, "airflow-temperature-celsius") ||
1203
!strcmp(a->name, "temperature-celsius") ||
1204
!strcmp(a->name, "temperature-celsius-2"))
1205
a->pretty_value = (fourtyeight & 0xFFFF)*1000 + 273150;
1206
else if (!strcmp(a->name, "temperature-centi-celsius"))
1207
a->pretty_value = (fourtyeight & 0xFFFF)*100 + 273150;
1208
else if (!strcmp(a->name, "power-on-minutes"))
1209
a->pretty_value = fourtyeight * 60 * 1000;
1210
else if (!strcmp(a->name, "power-on-seconds") ||
1211
!strcmp(a->name, "power-on-seconds-2"))
1212
a->pretty_value = fourtyeight * 1000;
1213
else if (!strcmp(a->name, "power-on-half-minutes"))
1214
a->pretty_value = fourtyeight * 30 * 1000;
1215
else if (!strcmp(a->name, "power-on-hours") ||
1216
!strcmp(a->name, "loaded-hours") ||
1217
!strcmp(a->name, "head-flying-hours"))
1218
a->pretty_value = (fourtyeight & 0xFFFFFFFFU) * 60 * 60 * 1000;
1219
else if (!strcmp(a->name, "reallocated-sector-count") ||
1220
!strcmp(a->name, "current-pending-sector"))
1221
a->pretty_value = fourtyeight & 0xFFFFFFFFU;
1222
else if (!strcmp(a->name, "endurance-remaining") ||
1223
!strcmp(a->name, "available-reserved-space"))
1224
a->pretty_value = a->current_value;
1225
else if (!strcmp(a->name, "total-lbas-written") ||
1226
!strcmp(a->name, "total-lbas-read"))
1227
a->pretty_value = fourtyeight * 65536LLU * 512LLU / 1000000LLU;
1228
else if (!strcmp(a->name, "timed-workload-media-wear") ||
1229
!strcmp(a->name, "timed-workload-host-reads"))
1230
a->pretty_value = (double)fourtyeight / 1024LLU;
1231
else if (!strcmp(a->name, "workload-timer"))
1232
a->pretty_value = fourtyeight * 60 * 1000;
1234
a->pretty_value = fourtyeight;
1237
typedef void (*SkSmartAttributeVerify)(SkDisk *d, SkSmartAttributeParsedData *a);
1239
typedef struct SkSmartAttributeInfo {
1241
SkSmartAttributeUnit unit;
1242
SkSmartAttributeVerify verify;
1243
} SkSmartAttributeInfo;
1245
static void verify_temperature(SkDisk *d, SkSmartAttributeParsedData *a) {
1247
assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_MKELVIN);
1249
if (a->pretty_value < SK_MKELVIN_VALID_MIN ||
1250
a->pretty_value > SK_MKELVIN_VALID_MAX) {
1251
a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1252
d->attribute_verification_bad = TRUE;
1256
static void verify_short_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_SHORT_MAX) {
1262
a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1263
d->attribute_verification_bad = TRUE;
1267
static void verify_long_time(SkDisk *d, SkSmartAttributeParsedData *a) {
1269
assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_MSECONDS);
1271
if (a->pretty_value < SK_MSECOND_VALID_MIN ||
1272
a->pretty_value > SK_MSECOND_VALID_LONG_MAX) {
1273
a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1274
d->attribute_verification_bad = TRUE;
1278
static void verify_sectors(SkDisk *d, SkSmartAttributeParsedData *a) {
1279
uint64_t max_sectors;
1283
assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_SECTORS);
1285
max_sectors = d->size / 512ULL;
1287
if (a->pretty_value == 0xffffffffULL ||
1288
a->pretty_value == 0xffffffffffffULL ||
1289
(max_sectors > 0 && a->pretty_value > max_sectors)) {
1290
a->pretty_value = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1291
d->attribute_verification_bad = TRUE;
1293
if ((!strcmp(a->name, "reallocated-sector-count") ||
1294
!strcmp(a->name, "current-pending-sector")) &&
1295
a->pretty_value > 0)
1300
/* This data is stolen from smartmontools */
1302
/* %STRINGPOOLSTART% */
1303
static const SkSmartAttributeInfo const attribute_info[256] = {
1304
[1] = { "raw-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1305
[2] = { "throughput-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1306
[3] = { "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_short_time },
1307
[4] = { "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1308
[5] = { "reallocated-sector-count", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1309
[6] = { "read-channel-margin", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1310
[7] = { "seek-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1311
[8] = { "seek-time-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1312
[9] = { "power-on-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1313
[10] = { "spin-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1314
[11] = { "calibration-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1315
[12] = { "power-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1316
[13] = { "read-soft-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1317
[170] = { "available-reserved-space", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL },
1318
[171] = { "program-fail-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1319
[172] = { "erase-fail-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1320
[184] = { "end-to-end-error", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1321
[187] = { "reported-uncorrect", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1322
[188] = { "command-timeout", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1323
[189] = { "high-fly-writes", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1324
[190] = { "airflow-temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1325
[191] = { "g-sense-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1326
[192] = { "power-off-retract-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1327
[193] = { "load-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1328
[194] = { "temperature-celsius-2", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1329
[195] = { "hardware-ecc-recovered", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1330
[196] = { "reallocated-event-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1331
[197] = { "current-pending-sector", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1332
[198] = { "offline-uncorrectable", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1333
[199] = { "udma-crc-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1334
[200] = { "multi-zone-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1335
[201] = { "soft-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1336
[202] = { "ta-increase-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1337
[203] = { "run-out-cancel", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1338
[204] = { "shock-count-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1339
[205] = { "shock-rate-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1340
[206] = { "flying-height", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1341
[207] = { "spin-high-current", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1342
[208] = { "spin-buzz", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1343
[209] = { "offline-seek-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1344
[220] = { "disk-shift", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1345
[221] = { "g-sense-error-rate-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1346
[222] = { "loaded-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1347
[223] = { "load-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1348
[224] = { "load-friction", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1349
[225] = { "load-cycle-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1350
[226] = { "load-in-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_short_time },
1351
[227] = { "torq-amp-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1352
[228] = { "power-off-retract-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1353
[230] = { "head-amplitude", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1354
[231] = { "temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1356
/* http://www.adtron.com/pdf/SMART_for_XceedLite_SATA_RevA.pdf */
1357
[232] = { "endurance-remaining", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL },
1358
[233] = { "power-on-seconds-2", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1359
[234] = { "uncorrectable-ecc-count", SK_SMART_ATTRIBUTE_UNIT_SECTORS, NULL },
1360
[235] = { "good-block-rate", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1362
[240] = { "head-flying-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1363
[241] = { "total-lbas-written", SK_SMART_ATTRIBUTE_UNIT_MB, NULL },
1364
[242] = { "total-lbas-read", SK_SMART_ATTRIBUTE_UNIT_MB, NULL },
1365
[250] = { "read-error-retry-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }
1367
/* %STRINGPOOLSTOP% */
1369
typedef enum SkSmartQuirk {
1370
SK_SMART_QUIRK_9_POWERONMINUTES = 0x000001,
1371
SK_SMART_QUIRK_9_POWERONSECONDS = 0x000002,
1372
SK_SMART_QUIRK_9_POWERONHALFMINUTES = 0x000004,
1373
SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT = 0x000008,
1374
SK_SMART_QUIRK_193_LOADUNLOAD = 0x000010,
1375
SK_SMART_QUIRK_194_10XCELSIUS = 0x000020,
1376
SK_SMART_QUIRK_194_UNKNOWN = 0x000040,
1377
SK_SMART_QUIRK_200_WRITEERRORCOUNT = 0x000080,
1378
SK_SMART_QUIRK_201_DETECTEDTACOUNT = 0x000100,
1379
SK_SMART_QUIRK_5_UNKNOWN = 0x000200,
1380
SK_SMART_QUIRK_9_UNKNOWN = 0x000400,
1381
SK_SMART_QUIRK_197_UNKNOWN = 0x000800,
1382
SK_SMART_QUIRK_198_UNKNOWN = 0x001000,
1383
SK_SMART_QUIRK_190_UNKNOWN = 0x002000,
1384
SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE = 0x004000,
1385
SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR = 0x008000,
1386
SK_SMART_QUIRK_225_TOTALLBASWRITTEN = 0x010000,
1387
SK_SMART_QUIRK_4_UNUSED = 0x020000,
1388
SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR = 0x040000,
1389
SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS = 0x080000,
1390
SK_SMART_QUIRK_228_WORKLOADTIMER = 0x100000,
1391
SK_SMART_QUIRK_3_UNUSED = 0x200000
1394
/* %STRINGPOOLSTART% */
1395
static const char *quirk_name[] = {
1398
"9_POWERONHALFMINUTES",
1399
"192_EMERGENCYRETRACTCYCLECT",
1403
"200_WRITEERRORCOUNT",
1404
"201_DETECTEDTACOUNT",
1410
"232_AVAILABLERESERVEDSPACE",
1411
"233_MEDIAWEAROUTINDICATOR",
1412
"225_TOTALLBASWRITTEN",
1414
"226_TIMEWORKLOADMEDIAWEAR",
1415
"227_TIMEWORKLOADHOSTREADS",
1416
"228_WORKLOADTIMER",
1420
/* %STRINGPOOLSTOP% */
1422
typedef struct SkSmartQuirkDatabase {
1424
const char *firmware;
1426
} SkSmartQuirkDatabase;
1428
static const SkSmartQuirkDatabase quirk_database[] = { {
1432
"FUJITSU MHY2120BH|"
1435
"^0085000B$", /* seems to be specific to this firmware */
1436
SK_SMART_QUIRK_9_POWERONMINUTES|
1437
SK_SMART_QUIRK_197_UNKNOWN|
1438
SK_SMART_QUIRK_198_UNKNOWN
1440
"^FUJITSU MHR2040AT$",
1442
SK_SMART_QUIRK_9_POWERONSECONDS|
1443
SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1444
SK_SMART_QUIRK_200_WRITEERRORCOUNT
1446
"^FUJITSU MHS20[6432]0AT( .)?$",
1448
SK_SMART_QUIRK_9_POWERONSECONDS|
1449
SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1450
SK_SMART_QUIRK_200_WRITEERRORCOUNT|
1451
SK_SMART_QUIRK_201_DETECTEDTACOUNT
1455
"FUJITSU MHG2...ATU?.*|"
1456
"FUJITSU MHH2...ATU?.*|"
1457
"FUJITSU MHJ2...ATU?.*|"
1458
"FUJITSU MHK2...ATU?.*|"
1459
"FUJITSU MHL2300AT|"
1460
"FUJITSU MHM2(20|15|10|06)0AT|"
1461
"FUJITSU MHN2...AT|"
1462
"FUJITSU MHR2020AT|"
1463
"FUJITSU MHT2...(AH|AS|AT|BH)U?.*|"
1464
"FUJITSU MHU2...ATU?.*|"
1465
"FUJITSU MHV2...(AH|AS|AT|BH|BS|BT).*|"
1466
"FUJITSU MP[A-G]3...A[HTEV]U?.*"
1469
SK_SMART_QUIRK_9_POWERONSECONDS
1475
"SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]"
1478
SK_SMART_QUIRK_9_POWERONHALFMINUTES
1485
SK_SMART_QUIRK_9_POWERONHALFMINUTES|
1486
SK_SMART_QUIRK_194_10XCELSIUS
1488
"^SAMSUNG SP40A2H$",
1490
SK_SMART_QUIRK_9_POWERONHALFMINUTES
1492
"^SAMSUNG SP80A4H$",
1494
SK_SMART_QUIRK_9_POWERONHALFMINUTES
1496
"^SAMSUNG SP8004H$",
1498
SK_SMART_QUIRK_9_POWERONHALFMINUTES
1503
"Maxtor 2B0(0[468]|1[05]|20)H1|"
1504
"Maxtor 4G(120J6|160J[68])|"
1505
"Maxtor 4D0(20H1|40H2|60H3|80H4)"
1508
SK_SMART_QUIRK_9_POWERONMINUTES|
1509
SK_SMART_QUIRK_194_UNKNOWN
1512
"Maxtor 2F0[234]0[JL]0|"
1513
"Maxtor 8(1280A2|2160A4|2560A4|3840A6|4000A6|5120A8)|"
1514
"Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)|"
1515
"Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)|"
1516
"Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)|"
1517
"Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)|"
1518
"Maxtor 9(2732U8|2390U7|204[09]U6|1707U5|1366U4|1024U3|0845U3|0683U2)|"
1519
"Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)|"
1520
"Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D[34]|90432D2)|"
1521
"Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)|"
1522
"Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)|"
1523
"Maxtor (33073U4|32049U3|31536U2|30768U1|33073H4|32305H3|31536H2|30768H1)|"
1524
"Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)|"
1525
"Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)|"
1526
"Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)|"
1527
"Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?|"
1528
"Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)|"
1529
"Maxtor 9(1023U2|1536U2|2049U3|2305U3|3073U4|4610U6|6147U8)|"
1530
"Maxtor 9(1023H2|1536H2|2049H3|2305H3|3073H4|4098H6|4610H6|6147H8)|"
1531
"Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)|"
1532
"Maxtor (98196H8|96147H6)|"
1533
"Maxtor 4W(100H6|080H6|060H4|040H3|030H2)|"
1534
"Maxtor 6(E0[234]|K04)0L0|"
1535
"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|"
1536
"Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)|"
1537
"Maxtor 7Y250[PM]0|"
1538
"Maxtor [45]A(25|30|32)0[JN]0|"
1539
"Maxtor 7L(25|30)0[SR]0"
1542
SK_SMART_QUIRK_9_POWERONMINUTES
1548
"HITACHI_DK14FA-20B|"
1549
"HITACHI_DK23..-..B?|"
1550
"HITACHI_DK23FA-20J|HTA422020F9AT[JN]0|"
1551
"HE[JN]4230[23]0F9AT00|"
1552
"HTC4260[23]0G5CE00|HTC4260[56]0G8CE00"
1555
SK_SMART_QUIRK_9_POWERONMINUTES|
1556
SK_SMART_QUIRK_193_LOADUNLOAD
1558
"^HTS541010G9SA00$",
1560
SK_SMART_QUIRK_5_UNKNOWN
1563
/*** Apple SSD (?) http://bugs.freedesktop.org/show_bug.cgi?id=24700
1564
https://bugs.launchpad.net/ubuntu/+source/gnome-disk-utility/+bug/438136/comments/4 */
1567
SK_SMART_QUIRK_5_UNKNOWN|
1568
SK_SMART_QUIRK_190_UNKNOWN
1572
"^INTEL SSDSA2CW[0-9]{3}G3$",
1574
SK_SMART_QUIRK_3_UNUSED|
1575
SK_SMART_QUIRK_4_UNUSED|
1576
SK_SMART_QUIRK_225_TOTALLBASWRITTEN|
1577
SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR|
1578
SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS|
1579
SK_SMART_QUIRK_228_WORKLOADTIMER|
1580
SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE|
1581
SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR
1589
static int match(const char*regex, const char *s, SkBool *result) {
1595
if (regcomp(&re, regex, REG_EXTENDED|REG_NOSUB) != 0) {
1600
if ((k = regexec(&re, s, 0, NULL, 0)) != 0) {
1602
if (k != REG_NOMATCH) {
1616
static int lookup_quirks(const char *model, const char *firmware, SkSmartQuirk *quirk) {
1618
const SkSmartQuirkDatabase *db;
1622
for (db = quirk_database; db->model || db->firmware; db++) {
1625
SkBool matching = FALSE;
1627
if ((k = match(db->model, model, &matching)) < 0)
1635
SkBool matching = FALSE;
1637
if ((k = match(db->firmware, firmware, &matching)) < 0)
1651
static const SkSmartAttributeInfo *lookup_attribute(SkDisk *d, uint8_t id) {
1652
const SkIdentifyParsedData *ipd;
1653
SkSmartQuirk quirk = 0;
1655
/* These are the complex ones */
1656
if (sk_disk_identify_parse(d, &ipd) < 0)
1659
if (lookup_quirks(ipd->model, ipd->firmware, &quirk) < 0)
1665
/* %STRINGPOOLSTART% */
1666
if (quirk & SK_SMART_QUIRK_3_UNUSED) {
1667
static const SkSmartAttributeInfo a = {
1668
"spin-up-time", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL
1672
/* %STRINGPOOLSTOP% */
1677
/* %STRINGPOOLSTART% */
1678
if (quirk & SK_SMART_QUIRK_4_UNUSED) {
1679
static const SkSmartAttributeInfo a = {
1680
"start-stop-count", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL
1684
/* %STRINGPOOLSTOP% */
1689
if (quirk & SK_SMART_QUIRK_5_UNKNOWN)
1695
/* %STRINGPOOLSTART% */
1696
if (quirk & SK_SMART_QUIRK_9_POWERONMINUTES) {
1697
static const SkSmartAttributeInfo a = {
1698
"power-on-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1702
} else if (quirk & SK_SMART_QUIRK_9_POWERONSECONDS) {
1703
static const SkSmartAttributeInfo a = {
1704
"power-on-seconds", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1708
} else if (quirk & SK_SMART_QUIRK_9_POWERONHALFMINUTES) {
1709
static const SkSmartAttributeInfo a = {
1710
"power-on-half-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1713
} else if (quirk & SK_SMART_QUIRK_9_UNKNOWN)
1715
/* %STRINGPOOLSTOP% */
1720
if (quirk & SK_SMART_QUIRK_190_UNKNOWN)
1726
/* %STRINGPOOLSTART% */
1727
if (quirk & SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) {
1728
static const SkSmartAttributeInfo a = {
1729
"emergency-retract-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1733
/* %STRINGPOOLSTOP% */
1738
/* %STRINGPOOLSTART% */
1739
if (quirk & SK_SMART_QUIRK_194_10XCELSIUS) {
1740
static const SkSmartAttributeInfo a = {
1741
"temperature-centi-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature
1744
} else if (quirk & SK_SMART_QUIRK_194_UNKNOWN)
1746
/* %STRINGPOOLSTOP% */
1751
if (quirk & SK_SMART_QUIRK_197_UNKNOWN)
1757
if (quirk & SK_SMART_QUIRK_198_UNKNOWN)
1763
/* %STRINGPOOLSTART% */
1764
if (quirk & SK_SMART_QUIRK_200_WRITEERRORCOUNT) {
1765
static const SkSmartAttributeInfo a = {
1766
"write-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1770
/* %STRINGPOOLSTOP% */
1775
/* %STRINGPOOLSTART% */
1776
if (quirk & SK_SMART_QUIRK_201_DETECTEDTACOUNT) {
1777
static const SkSmartAttributeInfo a = {
1778
"detected-ta-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1782
/* %STRINGPOOLSTOP% */
1787
/* %STRINGPOOLSTART% */
1788
if (quirk & SK_SMART_QUIRK_225_TOTALLBASWRITTEN) {
1789
static const SkSmartAttributeInfo a = {
1790
"total-lbas-written", SK_SMART_ATTRIBUTE_UNIT_MB, NULL
1794
/* %STRINGPOOLSTOP% */
1799
/* %STRINGPOOLSTART% */
1800
if (quirk & SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR) {
1801
static const SkSmartAttributeInfo a = {
1802
"timed-workload-media-wear", SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT, NULL
1806
/* %STRINGPOOLSTOP% */
1811
/* %STRINGPOOLSTART% */
1812
if (quirk & SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS) {
1813
static const SkSmartAttributeInfo a = {
1814
"timed-workload-host-reads", SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT, NULL
1818
/* %STRINGPOOLSTOP% */
1823
/* %STRINGPOOLSTART% */
1824
if (quirk & SK_SMART_QUIRK_228_WORKLOADTIMER) {
1825
static const SkSmartAttributeInfo a = {
1826
"workload-timer", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, NULL
1830
/* %STRINGPOOLSTOP% */
1835
/* %STRINGPOOLSTART% */
1836
if (quirk & SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE) {
1837
static const SkSmartAttributeInfo a = {
1838
"available-reserved-space", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL
1842
/* %STRINGPOOLSTOP% */
1846
/* %STRINGPOOLSTART% */
1847
if (quirk & SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR) {
1848
static const SkSmartAttributeInfo a = {
1849
"media-wearout-indicator", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL
1853
/* %STRINGPOOLSTOP% */
1859
/* These are the simple cases */
1860
if (attribute_info[id].name)
1861
return &attribute_info[id];
1866
int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **spd) {
1868
if (!d->smart_data_valid) {
1873
switch (d->smart_data[362]) {
1876
d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER;
1881
d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS;
1885
d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS;
1890
d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED;
1895
d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED;
1900
d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL;
1904
d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN;
1908
d->smart_parsed_data.self_test_execution_percent_remaining = 10*(d->smart_data[363] & 0xF);
1909
d->smart_parsed_data.self_test_execution_status = (d->smart_data[363] >> 4) & 0xF;
1911
d->smart_parsed_data.total_offline_data_collection_seconds = (uint16_t) d->smart_data[364] | ((uint16_t) d->smart_data[365] << 8);
1913
d->smart_parsed_data.conveyance_test_available = disk_smart_is_conveyance_test_available(d);
1914
d->smart_parsed_data.short_and_extended_test_available = disk_smart_is_short_and_extended_test_available(d);
1915
d->smart_parsed_data.start_test_available = disk_smart_is_start_test_available(d);
1916
d->smart_parsed_data.abort_test_available = disk_smart_is_abort_test_available(d);
1918
d->smart_parsed_data.short_test_polling_minutes = d->smart_data[372];
1919
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]);
1920
d->smart_parsed_data.conveyance_test_polling_minutes = d->smart_data[374];
1922
*spd = &d->smart_parsed_data;
1927
static void find_threshold(SkDisk *d, SkSmartAttributeParsedData *a) {
1931
if (!d->smart_thresholds_valid)
1934
for (n = 0, p = d->smart_thresholds+2; n < 30; n++, p+=12)
1941
a->threshold = p[1];
1942
a->threshold_valid = p[1] != 0xFE;
1944
a->good_now_valid = FALSE;
1946
a->good_in_the_past_valid = FALSE;
1947
a->good_in_the_past = TRUE;
1949
/* Always-Fail and Always-Passing thresholds are not relevant
1950
* for our assessment. */
1951
if (p[1] >= 1 && p[1] <= 0xFD) {
1953
if (a->worst_value_valid) {
1954
a->good_in_the_past = a->good_in_the_past && (a->worst_value > a->threshold);
1955
a->good_in_the_past_valid = TRUE;
1958
if (a->current_value_valid) {
1959
a->good_now = a->good_now && (a->current_value > a->threshold);
1960
a->good_now_valid = TRUE;
1965
(a->good_now_valid && !a->good_now) ||
1966
(a->good_in_the_past_valid && !a->good_in_the_past);
1971
a->threshold_valid = FALSE;
1972
a->good_now_valid = FALSE;
1973
a->good_in_the_past_valid = FALSE;
1977
int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, void* userdata) {
1981
if (!d->smart_data_valid) {
1986
for (n = 0, p = d->smart_data + 2; n < 30; n++, p+=12) {
1987
SkSmartAttributeParsedData a;
1988
const SkSmartAttributeInfo *i;
1994
memset(&a, 0, sizeof(a));
1996
a.current_value = p[3];
1997
a.current_value_valid = p[3] >= 1 && p[3] <= 0xFD;
1998
a.worst_value = p[4];
1999
a.worst_value_valid = p[4] >= 1 && p[4] <= 0xFD;
2001
a.flags = ((uint16_t) p[2] << 8) | p[1];
2002
a.prefailure = !!(p[1] & 1);
2003
a.online = !!(p[1] & 2);
2005
memcpy(a.raw, p+5, 6);
2007
if ((i = lookup_attribute(d, p[0]))) {
2008
a.name = _P(i->name);
2009
a.pretty_unit = i->unit;
2011
if (asprintf(&an, "attribute-%u", a.id) < 0) {
2017
a.pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
2022
find_threshold(d, &a);
2027
cb(d, &a, userdata);
2034
static const char *yes_no(SkBool b) {
2035
return b ? "yes" : "no";
2038
const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit) {
2040
/* %STRINGPOOLSTART% */
2041
const char * const map[] = {
2042
[SK_SMART_ATTRIBUTE_UNIT_UNKNOWN] = NULL,
2043
[SK_SMART_ATTRIBUTE_UNIT_NONE] = "",
2044
[SK_SMART_ATTRIBUTE_UNIT_MSECONDS] = "ms",
2045
[SK_SMART_ATTRIBUTE_UNIT_SECTORS] = "sectors",
2046
[SK_SMART_ATTRIBUTE_UNIT_MKELVIN] = "mK",
2047
[SK_SMART_ATTRIBUTE_UNIT_PERCENT] = "%",
2048
[SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT] = "%",
2049
[SK_SMART_ATTRIBUTE_UNIT_MB] = "MB"
2051
/* %STRINGPOOLSTOP% */
2053
if (unit >= _SK_SMART_ATTRIBUTE_UNIT_MAX)
2056
return _P(map[unit]);
2059
struct attr_helper {
2064
static void temperature_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2066
if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MKELVIN)
2069
if (!strcmp(a->name, "temperature-centi-celsius") ||
2070
!strcmp(a->name, "temperature-celsius") ||
2071
!strcmp(a->name, "temperature-celsius-2") ||
2072
!strcmp(a->name, "airflow-temperature-celsius")) {
2074
if (!ah->found || a->pretty_value > *ah->value)
2075
*ah->value = a->pretty_value;
2081
int sk_disk_smart_get_temperature(SkDisk *d, uint64_t *kelvin) {
2082
struct attr_helper ah;
2090
if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) temperature_cb, &ah) < 0)
2101
static void power_on_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2103
if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MSECONDS)
2106
if (!strcmp(a->name, "power-on-minutes") ||
2107
!strcmp(a->name, "power-on-seconds") ||
2108
!strcmp(a->name, "power-on-seconds-2") ||
2109
!strcmp(a->name, "power-on-half-minutes") ||
2110
!strcmp(a->name, "power-on-hours")) {
2112
if (!ah->found || a->pretty_value > *ah->value)
2113
*ah->value = a->pretty_value;
2119
int sk_disk_smart_get_power_on(SkDisk *d, uint64_t *mseconds) {
2120
struct attr_helper ah;
2126
ah.value = mseconds;
2128
if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_on_cb, &ah) < 0)
2139
static void power_cycle_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2141
if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_NONE)
2144
if (!strcmp(a->name, "power-cycle-count")) {
2146
if (!ah->found || a->pretty_value > *ah->value)
2147
*ah->value = a->pretty_value;
2153
int sk_disk_smart_get_power_cycle(SkDisk *d, uint64_t *count) {
2154
struct attr_helper ah;
2162
if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_cycle_cb, &ah) < 0)
2173
static void fill_cache_cb(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) {
2175
if (a->prefailure) {
2176
if (a->good_now_valid && !a->good_now)
2177
d->bad_attribute_now = TRUE;
2179
if (a->good_in_the_past_valid && !a->good_in_the_past)
2180
d->bad_attribute_in_the_past = TRUE;
2183
if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
2186
if (!a->current_value_valid)
2189
if (!strcmp(a->name, "reallocated-sector-count")) {
2190
if (a->pretty_value > d->reallocated_sector_count)
2191
d->reallocated_sector_count = a->pretty_value;
2192
d->reallocated_sector_count_found = TRUE;
2193
if (a->good_now_valid && !a->good_now)
2194
d->reallocated_sector_count_bad = TRUE;
2197
if (!strcmp(a->name, "current-pending-sector")) {
2198
if (a->pretty_value > d->current_pending_sector)
2199
d->current_pending_sector = a->pretty_value;
2200
d->current_pending_sector_found = TRUE;
2201
if (a->good_now_valid && !a->good_now)
2202
d->current_pending_sector_bad = 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
int sk_disk_smart_get_overall(SkDisk *d, SkSmartOverall *overall) {
2265
/* First, check SMART self-assesment */
2266
if (sk_disk_smart_status(d, &good) < 0)
2270
*overall = SK_SMART_OVERALL_BAD_STATUS;
2274
/* Second, check if the number of bad sectors is greater than
2275
* a certain threshold */
2276
if (sk_disk_smart_get_bad(d, §ors) < 0) {
2277
if (errno != ENOENT)
2281
if (d->reallocated_sector_count_bad || d->current_pending_sector_bad) {
2282
*overall = SK_SMART_OVERALL_BAD_SECTOR_MANY;
2287
/* Third, check if any of the SMART attributes is bad */
2288
if (fill_cache (d) < 0)
2291
if (d->bad_attribute_now) {
2292
*overall = SK_SMART_OVERALL_BAD_ATTRIBUTE_NOW;
2296
/* Fourth, check if there are any bad sectors at all */
2298
*overall = SK_SMART_OVERALL_BAD_SECTOR;
2302
/* Fifth, check if any of the SMART attributes ever was bad */
2303
if (d->bad_attribute_in_the_past) {
2304
*overall = SK_SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST;
2308
/* Sixth, there's really nothing to complain about, so give it a pass */
2309
*overall = SK_SMART_OVERALL_GOOD;
2313
static char* print_name(char *s, size_t len, uint8_t id, const char *k) {
2318
snprintf(s, len, "%u", id);
2325
static char *print_value(char *s, size_t len, uint64_t pretty_value, SkSmartAttributeUnit pretty_unit) {
2327
switch (pretty_unit) {
2328
case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
2330
if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU)
2331
snprintf(s, len, "%0.1f years", ((double) pretty_value)/(1000.0*60*60*24*365));
2332
else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU)
2333
snprintf(s, len, "%0.1f months", ((double) pretty_value)/(1000.0*60*60*24*30));
2334
else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU)
2335
snprintf(s, len, "%0.1f days", ((double) pretty_value)/(1000.0*60*60*24));
2336
else if (pretty_value >= 1000LLU*60LLU*60LLU)
2337
snprintf(s, len, "%0.1f h", ((double) pretty_value)/(1000.0*60*60));
2338
else if (pretty_value >= 1000LLU*60LLU)
2339
snprintf(s, len, "%0.1f min", ((double) pretty_value)/(1000.0*60));
2340
else if (pretty_value >= 1000LLU)
2341
snprintf(s, len, "%0.1f s", ((double) pretty_value)/(1000.0));
2343
snprintf(s, len, "%llu ms", (unsigned long long) pretty_value);
2347
case SK_SMART_ATTRIBUTE_UNIT_MKELVIN:
2348
snprintf(s, len, "%0.1f C", ((double) pretty_value - 273150) / 1000);
2351
case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
2352
snprintf(s, len, "%llu sectors", (unsigned long long) pretty_value);
2355
case SK_SMART_ATTRIBUTE_UNIT_PERCENT:
2356
snprintf(s, len, "%llu%%", (unsigned long long) pretty_value);
2359
case SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT:
2360
snprintf(s, len, "%0.3f%%", (double) pretty_value);
2363
case SK_SMART_ATTRIBUTE_UNIT_MB:
2364
if (pretty_value >= 1000000LLU)
2365
snprintf(s, len, "%0.3f TB", (double) pretty_value / 1000000LLU);
2366
else if (pretty_value >= 1000LLU)
2367
snprintf(s, len, "%0.3f GB", (double) pretty_value / 1000LLU);
2369
snprintf(s, len, "%llu MB", (unsigned long long) pretty_value);
2372
case SK_SMART_ATTRIBUTE_UNIT_NONE:
2373
snprintf(s, len, "%llu", (unsigned long long) pretty_value);
2376
case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
2377
snprintf(s, len, "n/a");
2380
case _SK_SMART_ATTRIBUTE_UNIT_MAX:
2389
#define HIGHLIGHT "\x1B[1m"
2390
#define ENDHIGHLIGHT "\x1B[0m"
2392
static void disk_dump_attributes(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) {
2395
char tt[32], tw[32], tc[32];
2398
snprintf(tt, sizeof(tt), "%3u", a->threshold);
2399
tt[sizeof(tt)-1] = 0;
2400
snprintf(tw, sizeof(tw), "%3u", a->worst_value);
2401
tw[sizeof(tw)-1] = 0;
2402
snprintf(tc, sizeof(tc), "%3u", a->current_value);
2403
tc[sizeof(tc)-1] = 0;
2405
highlight = a->warn && isatty(1);
2408
fprintf(stderr, HIGHLIGHT);
2410
printf("%3u %-27s %-3s %-3s %-3s %-11s 0x%02x%02x%02x%02x%02x%02x %-7s %-7s %-4s %-4s\n",
2412
print_name(name, sizeof(name), a->id, a->name),
2413
a->current_value_valid ? tc : "n/a",
2414
a->worst_value_valid ? tw : "n/a",
2415
a->threshold_valid ? tt : "n/a",
2416
print_value(pretty, sizeof(pretty), a->pretty_value, a->pretty_unit),
2417
a->raw[0], a->raw[1], a->raw[2], a->raw[3], a->raw[4], a->raw[5],
2418
a->prefailure ? "prefail" : "old-age",
2419
a->online ? "online" : "offline",
2420
a->good_now_valid ? yes_no(a->good_now) : "n/a",
2421
a->good_in_the_past_valid ? yes_no(a->good_in_the_past) : "n/a");
2424
fprintf(stderr, ENDHIGHLIGHT);
2427
int sk_disk_dump(SkDisk *d) {
2429
SkBool awake = FALSE;
2434
printf("Device: %s%s%s\n"
2436
d->name ? disk_type_to_prefix_string(d->type) : "",
2438
d->name ? d->name : "n/a",
2439
disk_type_to_human_string(d->type));
2441
ret = sk_disk_get_size(d, &size);
2443
printf("Size: %lu MiB\n", (unsigned long) (d->size/1024/1024));
2445
printf("Size: %s\n", strerror(errno));
2447
if (d->identify_valid) {
2448
const SkIdentifyParsedData *ipd;
2449
SkSmartQuirk quirk = 0;
2452
if ((ret = sk_disk_identify_parse(d, &ipd)) < 0)
2455
printf("Model: [%s]\n"
2458
"SMART Available: %s\n",
2462
yes_no(disk_smart_is_available(d)));
2464
if ((ret = lookup_quirks(ipd->model, ipd->firmware, &quirk)))
2469
for (i = 0; quirk_name[i]; i++)
2471
printf(" %s", _P(quirk_name[i]));
2476
ret = sk_disk_check_sleep_mode(d, &awake);
2477
printf("Awake: %s\n",
2478
ret >= 0 ? yes_no(awake) : strerror(errno));
2480
if (disk_smart_is_available(d)) {
2481
SkSmartOverall overall;
2482
const SkSmartParsedData *spd;
2485
uint64_t value, power_on;
2487
ret = sk_disk_smart_status(d, &good);
2488
printf("%sSMART Disk Health Good: %s%s\n",
2489
ret >= 0 && !good ? HIGHLIGHT : "",
2490
ret >= 0 ? yes_no(good) : strerror(errno),
2491
ret >= 0 && !good ? ENDHIGHLIGHT : "");
2492
if ((ret = sk_disk_smart_read_data(d)) < 0)
2495
if ((ret = sk_disk_smart_parse(d, &spd)) < 0)
2498
printf("Off-line Data Collection Status: [%s]\n"
2499
"Total Time To Complete Off-Line Data Collection: %u s\n"
2500
"Self-Test Execution Status: [%s]\n"
2501
"Percent Self-Test Remaining: %u%%\n"
2502
"Conveyance Self-Test Available: %s\n"
2503
"Short/Extended Self-Test Available: %s\n"
2504
"Start Self-Test Available: %s\n"
2505
"Abort Self-Test Available: %s\n"
2506
"Short Self-Test Polling Time: %u min\n"
2507
"Extended Self-Test Polling Time: %u min\n"
2508
"Conveyance Self-Test Polling Time: %u min\n",
2509
sk_smart_offline_data_collection_status_to_string(spd->offline_data_collection_status),
2510
spd->total_offline_data_collection_seconds,
2511
sk_smart_self_test_execution_status_to_string(spd->self_test_execution_status),
2512
spd->self_test_execution_percent_remaining,
2513
yes_no(spd->conveyance_test_available),
2514
yes_no(spd->short_and_extended_test_available),
2515
yes_no(spd->start_test_available),
2516
yes_no(spd->abort_test_available),
2517
spd->short_test_polling_minutes,
2518
spd->extended_test_polling_minutes,
2519
spd->conveyance_test_polling_minutes);
2521
if (sk_disk_smart_get_bad(d, &value) < 0)
2522
printf("Bad Sectors: %s\n", strerror(errno));
2524
printf("%sBad Sectors: %s%s\n",
2525
value > 0 ? HIGHLIGHT : "",
2526
print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_SECTORS),
2527
value > 0 ? ENDHIGHLIGHT : "");
2529
if (sk_disk_smart_get_power_on(d, &power_on) < 0) {
2530
printf("Powered On: %s\n", strerror(errno));
2533
printf("Powered On: %s\n", print_value(pretty, sizeof(pretty), power_on, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2535
if (sk_disk_smart_get_power_cycle(d, &value) < 0)
2536
printf("Power Cycles: %s\n", strerror(errno));
2538
printf("Power Cycles: %llu\n", (unsigned long long) value);
2540
if (value > 0 && power_on > 0)
2541
printf("Average Powered On Per Power Cycle: %s\n", print_value(pretty, sizeof(pretty), power_on/value, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2544
if (sk_disk_smart_get_temperature(d, &value) < 0)
2545
printf("Temperature: %s\n", strerror(errno));
2547
printf("Temperature: %s\n", print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_MKELVIN));
2549
printf("Attribute Parsing Verification: %s\n",
2550
d->attribute_verification_bad ? "Bad" : "Good");
2552
if (sk_disk_smart_get_overall(d, &overall) < 0)
2553
printf("Overall Status: %s\n", strerror(errno));
2555
printf("%sOverall Status: %s%s\n",
2556
overall != SK_SMART_OVERALL_GOOD ? HIGHLIGHT : "",
2557
sk_smart_overall_to_string(overall),
2558
overall != SK_SMART_OVERALL_GOOD ? ENDHIGHLIGHT : "");
2560
printf("%3s %-27s %5s %5s %5s %-11s %-14s %-7s %-7s %-4s %-4s\n",
2573
if ((ret = sk_disk_smart_parse_attributes(d, disk_dump_attributes, NULL)) < 0)
2576
printf("ATA SMART not supported.\n");
2581
int sk_disk_get_size(SkDisk *d, uint64_t *bytes) {
2585
if (d->size == (uint64_t) -1) {
2594
static int disk_find_type(SkDisk *d, dev_t devnum) {
2596
struct udev_device *dev = NULL, *usb;
2602
if (!(udev = udev_new())) {
2607
if (!(dev = udev_device_new_from_devnum(udev, 'b', devnum))) {
2612
if ((a = udev_device_get_property_value(dev, "ID_ATA_SMART_ACCESS"))) {
2615
for (u = 0; u < _SK_DISK_TYPE_MAX; u++) {
2618
if (!(t = disk_type_to_prefix_string(u)))
2621
if (!strcmp(a, t)) {
2628
d->type = SK_DISK_TYPE_NONE;
2633
if ((usb = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"))) {
2634
const char *product, *vendor;
2637
if (!(product = udev_device_get_sysattr_value(usb, "idProduct")) ||
2638
sscanf(product, "%04x", &pid) != 1) {
2643
if (!(vendor = udev_device_get_sysattr_value(usb, "idVendor")) ||
2644
sscanf(vendor, "%04x", &vid) != 1) {
2649
if ((vid == 0x0928 && pid == 0x0000))
2650
/* This Oxford Semiconductor bridge seems to
2651
* choke on SAT commands. Let's explicitly
2652
* black list it here.
2654
* http://bugs.freedesktop.org/show_bug.cgi?id=24951 */
2655
d->type = SK_DISK_TYPE_NONE;
2656
else if ((vid == 0x152d && pid == 0x2329) ||
2657
(vid == 0x152d && pid == 0x2338) ||
2658
(vid == 0x152d && pid == 0x2339))
2659
/* Some JMicron bridges seem to choke on SMART
2660
* commands, so let's explicitly black list
2663
* https://bugzilla.redhat.com/show_bug.cgi?id=515881
2665
* At least some of the JMicron bridges with
2666
* these vids/pids choke on the jmicron access
2667
* mode. To make sure we don't break things
2668
* for people we now disable this by
2670
d->type = SK_DISK_TYPE_NONE;
2671
else if ((vid == 0x152d && pid == 0x2336))
2672
/* This JMicron bridge seems to always work
2673
* with SMART commands send with the jmicron
2675
d->type = SK_DISK_TYPE_JMICRON;
2676
else if ((vid == 0x0c0b && pid == 0xb159) ||
2677
(vid == 0x04fc && pid == 0x0c25) ||
2678
(vid == 0x04fc && pid == 0x0c15))
2679
d->type = SK_DISK_TYPE_SUNPLUS;
2681
d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_12;
2683
} else if (udev_device_get_parent_with_subsystem_devtype(dev, "ide", NULL))
2684
d->type = SK_DISK_TYPE_LINUX_IDE;
2685
else if (udev_device_get_parent_with_subsystem_devtype(dev, "scsi", NULL))
2686
d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_16;
2688
d->type = SK_DISK_TYPE_AUTO;
2694
udev_device_unref(dev);
2702
static int init_smart(SkDisk *d) {
2703
/* We don't do the SMART initialization right-away, since some
2704
* drivers spin up when we do that */
2708
if (d->smart_initialized)
2711
d->smart_initialized = TRUE;
2713
/* Check if driver can do SMART, and enable if necessary */
2714
if (!disk_smart_is_available(d))
2717
if (!disk_smart_is_enabled(d)) {
2718
if ((ret = disk_smart_enable(d, TRUE)) < 0)
2721
if ((ret = disk_identify_device(d)) < 0)
2724
if (!disk_smart_is_enabled(d)) {
2731
disk_smart_read_thresholds(d);
2738
int sk_disk_open(const char *name, SkDisk **_d) {
2745
if (!(d = calloc(1, sizeof(SkDisk)))) {
2751
d->size = (uint64_t) -1;
2754
d->type = SK_DISK_TYPE_BLOB;
2758
d->type = SK_DISK_TYPE_AUTO;
2760
if (!(dn = disk_type_from_string(name, &d->type)))
2763
if (!(d->name = strdup(dn))) {
2768
if ((d->fd = open(d->name,
2769
O_RDONLY|O_NOCTTY|O_NONBLOCK
2779
if ((ret = fstat(d->fd, &st)) < 0)
2782
if (!S_ISBLK(st.st_mode)) {
2788
/* So, it's a block device. Let's make sure the ioctls work */
2789
if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0)
2792
if (d->size <= 0 || d->size == (uint64_t) -1) {
2798
/* OK, it's a real block device with a size. Now let's find the suitable API */
2799
if (d->type == SK_DISK_TYPE_AUTO)
2800
if ((ret = disk_find_type(d, st.st_rdev)) < 0)
2803
if (d->type == SK_DISK_TYPE_AUTO) {
2804
/* We have no clue, so let's autotest for a working API */
2805
for (d->type = 0; d->type < _SK_DISK_TYPE_TEST_MAX; d->type++)
2806
if (disk_identify_device(d) >= 0)
2808
if (d->type >= _SK_DISK_TYPE_TEST_MAX)
2809
d->type = SK_DISK_TYPE_NONE;
2811
disk_identify_device(d);
2826
void sk_disk_free(SkDisk *d) {
2837
int sk_disk_get_blob(SkDisk *d, const void **blob, size_t *rsize) {
2839
SkBool good, have_good = FALSE;
2847
(d->identify_valid ? 8 + sizeof(d->identify) : 0) +
2848
(d->smart_data_valid ? 8 + sizeof(d->smart_data) : 0) +
2849
(d->smart_thresholds_valid ? 8 + sizeof(d->smart_thresholds) : 0);
2851
if (sk_disk_smart_status(d, &good) >= 0) {
2862
if (!(d->blob = malloc(size))) {
2869
/* These memory accesses are only OK as long as all our
2870
* objects are sensibly aligned, which they are... */
2872
if (d->identify_valid) {
2873
p[0] = SK_BLOB_TAG_IDENTIFY;
2874
p[1] = htonl(sizeof(d->identify));
2877
memcpy(p, d->identify, sizeof(d->identify));
2878
p = (uint32_t*) ((uint8_t*) p + sizeof(d->identify));
2882
p[0] = SK_BLOB_TAG_SMART_STATUS;
2884
p[2] = htonl(!!good);
2888
if (d->smart_data_valid) {
2889
p[0] = SK_BLOB_TAG_SMART_DATA;
2890
p[1] = htonl(sizeof(d->smart_data));
2893
memcpy(p, d->smart_data, sizeof(d->smart_data));
2894
p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_data));
2897
if (d->smart_thresholds_valid) {
2898
p[0] = SK_BLOB_TAG_SMART_THRESHOLDS;
2899
p[1] = htonl(sizeof(d->smart_thresholds));
2902
memcpy(p, d->smart_thresholds, sizeof(d->smart_thresholds));
2903
p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_thresholds));
2906
assert((size_t) ((uint8_t*) p - (uint8_t*) d->blob) == size);
2914
int sk_disk_set_blob(SkDisk *d, const void *blob, size_t size) {
2917
SkBool idv = FALSE, sdv = FALSE, stv = FALSE, bssv = FALSE;
2922
if (d->type != SK_DISK_TYPE_BLOB) {
2932
/* First run, verify if everything makes sense */
2936
uint32_t tag, tsize;
2944
memcpy(&tsize, p+1, 4);
2948
if (left < ntohl(tsize)) {
2955
case SK_BLOB_TAG_IDENTIFY:
2956
if (ntohl(tsize) != sizeof(d->identify) || idv) {
2963
case SK_BLOB_TAG_SMART_STATUS:
2964
if (ntohl(tsize) != 4 || bssv) {
2971
case SK_BLOB_TAG_SMART_DATA:
2972
if (ntohl(tsize) != sizeof(d->smart_data) || sdv) {
2979
case SK_BLOB_TAG_SMART_THRESHOLDS:
2980
if (ntohl(tsize) != sizeof(d->smart_thresholds) || stv) {
2988
p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
2989
left -= ntohl(tsize);
2997
d->identify_valid = idv;
2998
d->smart_data_valid = sdv;
2999
d->smart_thresholds_valid = stv;
3000
d->blob_smart_status_valid = bssv;
3002
/* Second run, actually copy things in */
3006
uint32_t tag, tsize;
3010
memcpy(&tsize, p+1, 4);
3014
assert(left >= ntohl(tsize));
3018
case SK_BLOB_TAG_IDENTIFY:
3019
assert(ntohl(tsize) == sizeof(d->identify));
3020
memcpy(d->identify, p, sizeof(d->identify));
3023
case SK_BLOB_TAG_SMART_STATUS: {
3025
assert(ntohl(tsize) == 4);
3027
d->blob_smart_status = !!ok;
3031
case SK_BLOB_TAG_SMART_DATA:
3032
assert(ntohl(tsize) == sizeof(d->smart_data));
3033
memcpy(d->smart_data, p, sizeof(d->smart_data));
3036
case SK_BLOB_TAG_SMART_THRESHOLDS:
3037
assert(ntohl(tsize) == sizeof(d->smart_thresholds));
3038
memcpy(d->smart_thresholds, p, sizeof(d->smart_thresholds));
3042
p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
3043
left -= ntohl(tsize);