summaryrefslogtreecommitdiff
path: root/sbin
diff options
context:
space:
mode:
authorGrigoriy Orlov <gluk@cvs.openbsd.org>2002-06-08 12:50:43 +0000
committerGrigoriy Orlov <gluk@cvs.openbsd.org>2002-06-08 12:50:43 +0000
commitd4eeb0898268fefbf954dfb6408554c51628ff52 (patch)
tree1c09c99249dee40119a22f0d70e92c62a1754540 /sbin
parent46d9b4830762b6dab80f47b9f4560feb1dbd97c2 (diff)
- extended SMART support.
- style, typos. - Big part of program redesigned and become more clean and simple. Work done by Alexander Yurchenko <grange@openbsd.ru>. Readattr command implementation and some cleanups by me. Costa@ ok.
Diffstat (limited to 'sbin')
-rw-r--r--sbin/atactl/atactl.890
-rw-r--r--sbin/atactl/atactl.c888
-rw-r--r--sbin/atactl/atasmart.h217
3 files changed, 1046 insertions, 149 deletions
diff --git a/sbin/atactl/atactl.8 b/sbin/atactl/atactl.8
index cc34ad99b6b..d927cb29518 100644
--- a/sbin/atactl/atactl.8
+++ b/sbin/atactl/atactl.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: atactl.8,v 1.13 2002/04/30 04:25:28 fgsch Exp $
+.\" $OpenBSD: atactl.8,v 1.14 2002/06/08 12:50:42 gluk Exp $
.\" $NetBSD: atactl.8,v 1.5 1999/02/24 18:49:14 jwise Exp $
.\"
.\" Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -67,6 +67,7 @@ is specified without a
the
.Cm identify
command is implied.
+.Sh COMMANDS
.Pp
.Cm identify
.Pp
@@ -251,6 +252,11 @@ Disables support for SMART on the specified device.
Note that this means that the device will no longer record any SMART
information.
.Pp
+Note that SMART
+.Em must
+be enabled while executing the next described commands or the device will
+return an error.
+.Pp
.Cm smartstatus
.Pp
Reads the reliability status of the specified device.
@@ -259,9 +265,82 @@ that one of its thresholds is exceeded (a strong indication of imminent
failure), the warning
.Sq SMART threshold exceeded!
is printed to stderr and a status of 2 is returned.
-Note that SMART
-.Em must
-be enabled or the device will return an error.
+.Pp
+.Cm smartautosave
+.Ar enable | disable
+.Pp
+Enables/disables attribute autosave feature on the specified device.
+.Pp
+.Cm smartoffline
+.Ar subcommand
+.Pp
+Causes the specified device to immediately initiate the optional set of
+activities that collect SMART data in off-line mode and then save this data
+to the device's non-volatile memory, or execute a self-diagnostic test
+routines in either captive or off-line mode.
+The
+.Ar subcommand
+may be one of the following:
+.Bl -inset -offset indent
+.It Em collect
+start SMART off-line data collection immediately;
+.It Em shortoffline
+execute SMART short self-test routine immediately in off-line mode;
+.It Em extenoffline
+execute SMART extended self-test routine immediately in off-line mode;
+.It Em abort
+abort off-line mode self-test routine;
+.It Em shortcaptive
+execute SMART short self-test routine immediately in captive mode;
+.It Em extencaptive
+execute SMART extended self-test routine immediately in captive mode.
+.El
+.Pp
+Note that executing self-test routines in captive mode causes the device to
+be not accessible until the routine completes.
+This option is therefore
+.Em not recommended
+unless the implications are understood.
+.Pp
+.Cm smartread
+.Pp
+Reads various SMART information from the specified device and prints it to
+stdout.
+.Pp
+.Cm smartreadlog
+.Ar log
+.Pp
+Reads specified
+.Ar log
+and prints it to stdout.
+The
+.Ar log
+may by one of the following:
+.Bl -inset -offset indent
+.It Em directory
+error log directory;
+.It Em summary
+summary error log;
+.It Em comp
+comprehensive error log;
+.It Em selftest
+self-test log.
+.El
+.Pp
+.Cm readattr
+.Pp
+Displays attributes thresholds and values for the specified device.
+Besides attributes values device vendor can provide additional information
+shown in the last column ``Raw''.
+Attributes names can be completely wrong since they vary between vendors and
+even models, so don't rely on it.
+SMART must be enabled while executing this command or the device will return
+an error.
+.Pp
+SMART commands and
+.Cm readattr
+command are for experts only.
+If you can't understand its meaning don't ask us, read ATA working drafts.
.Pp
.Cm writecachedisable
.Pp
@@ -347,8 +426,5 @@ Disabling read look-head with the
.Cm readaheaddisable
might cause problems with mounted filesystems on that device.
.Pp
-There is no support for reading SMART logs or initiating a SMART
-selftest.
-.Pp
There is no support for the Secure Mode commands (in particular the
Security Erase Unit).
diff --git a/sbin/atactl/atactl.c b/sbin/atactl/atactl.c
index b153e6a0c4e..df1bdd00dbd 100644
--- a/sbin/atactl/atactl.c
+++ b/sbin/atactl/atactl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: atactl.c,v 1.14 2002/06/08 12:31:54 gluk Exp $ */
+/* $OpenBSD: atactl.c,v 1.15 2002/06/08 12:50:42 gluk Exp $ */
/* $NetBSD: atactl.c,v 1.4 1999/02/24 18:49:14 jwise Exp $ */
/*-
@@ -43,6 +43,7 @@
#include <sys/param.h>
#include <sys/ioctl.h>
+
#include <err.h>
#include <errno.h>
#include <fcntl.h>
@@ -56,6 +57,8 @@
#include <dev/ic/wdcreg.h>
#include <sys/ataio.h>
+#include "atasmart.h"
+
struct command {
const char *cmd_name;
void (*cmd_func)(int, char *[]);
@@ -66,15 +69,19 @@ struct bitinfo {
const char *string;
};
+struct valinfo {
+ int value;
+ const char *string;
+};
+
int main(int, char *[]);
void usage(void);
void ata_command(struct atareq *);
void print_bitinfo(const char *, u_int, struct bitinfo *);
+int strtoval(const char *, struct valinfo *);
+const char *valtostr(int, struct valinfo *);
int fd; /* file descriptor for device */
-const char *dvname; /* device name */
-char dvname_store[MAXPATHLEN]; /* for opendisk(3) */
-const char *cmdname; /* command user issued */
extern const char *__progname; /* from crt0.o */
@@ -86,7 +93,17 @@ void device_checkpower(int, char *[]);
void device_acoustic(int, char *[]);
void device_apm(int, char *[]);
void device_feature(int, char *[]);
-void device_smart(int, char *[]);
+void device_smart_enable(int, char *[]);
+void device_smart_disable(int, char *[]);
+void device_smart_status(int, char *[]);
+void device_smart_autosave(int, char *[]);
+void device_smart_offline(int, char *[]);
+void device_smart_read(int, char *[]);
+void device_smart_readlog(int, char *[]);
+void device_attr(int, char *[]);
+
+void smart_print_errdata(struct smart_log_errdata *);
+int smart_cksum(u_int8_t *, int);
struct command commands[] = {
{ "dump", device_dump },
@@ -108,9 +125,14 @@ struct command commands[] = {
{ "puisspinup", device_feature },
{ "readaheaddisable", device_feature },
{ "readaheadenable", device_feature },
- { "smartenable", device_smart },
- { "smartdisable", device_smart },
- { "smartstatus", device_smart },
+ { "smartenable", device_smart_enable },
+ { "smartdisable", device_smart_disable },
+ { "smartstatus", device_smart_status },
+ { "smartautosave", device_smart_autosave },
+ { "smartoffline", device_smart_offline },
+ { "smartread", device_smart_read },
+ { "smartreadlog", device_smart_readlog },
+ { "readattr", device_attr },
{ "writecachedisable", device_feature },
{ "writecacheenable", device_feature },
{ NULL, NULL },
@@ -188,32 +210,124 @@ struct bitinfo ata_cmd_ext[] = {
{ NULL, NULL },
};
+/*
+ * Tables containing bitmasks and values used for
+ * SMART commands.
+ */
+
+struct bitinfo smart_offcap[] = {
+ { SMART_OFFCAP_EXEC, "execute immediate" },
+ { SMART_OFFCAP_ABORT, "abort/restart" },
+ { SMART_OFFCAP_READSCAN, "read scanning" },
+ { SMART_OFFCAP_SELFTEST, "self-test routines" },
+ { 0, NULL}
+};
+
+struct bitinfo smart_smartcap[] = {
+ { SMART_SMARTCAP_SAVE, "saving SMART data" },
+ { SMART_SMARTCAP_AUTOSAVE, "enable/disable attribute autosave" },
+ { 0, NULL }
+};
+
+struct valinfo smart_autosave[] = {
+ { SMART_AUTOSAVE_EN, "enable" },
+ { SMART_AUTOSAVE_DS, "disable" },
+ { 0, NULL }
+};
+
+struct valinfo smart_offline[] = {
+ { SMART_OFFLINE_COLLECT, "collect" },
+ { SMART_OFFLINE_SHORTOFF, "shortoffline" },
+ { SMART_OFFLINE_EXTENOFF, "extenoffline" },
+ { SMART_OFFLINE_ABORT, "abort" },
+ { SMART_OFFLINE_SHORTCAP, "shortcaptive" },
+ { SMART_OFFLINE_EXTENCAP, "extencaptive" },
+ { 0, NULL }
+};
+
+struct valinfo smart_readlog[] = {
+ { SMART_READLOG_DIR, "directory" },
+ { SMART_READLOG_SUM, "summary" },
+ { SMART_READLOG_COMP, "comp" },
+ { SMART_READLOG_SELF, "selftest" },
+ { 0, NULL }
+};
+
+struct valinfo smart_offstat[] = {
+ { SMART_OFFSTAT_NOTSTART, "never started" },
+ { SMART_OFFSTAT_COMPLETE, "completed ok" },
+ { SMART_OFFSTAT_SUSPEND, "suspended by an interrupting command" },
+ { SMART_OFFSTAT_INTR, "aborted by an interrupting command" },
+ { SMART_OFFSTAT_ERROR, "aborted due to fatal error" },
+ { 0, NULL }
+};
+
+struct valinfo smart_selfstat[] = {
+ { SMART_SELFSTAT_COMPLETE, "completed ok or not started" },
+ { SMART_SELFSTAT_ABORT, "aborted" },
+ { SMART_SELFSTAT_INTR, "hardware or software reset" },
+ { SMART_SELFSTAT_ERROR, "fatal error" },
+ { SMART_SELFSTAT_UNKFAIL, "unknown test element failed" },
+ { SMART_SELFSTAT_ELFAIL, "electrical test element failed" },
+ { SMART_SELFSTAT_SRVFAIL, "servo test element failed" },
+ { SMART_SELFSTAT_RDFAIL, "read test element failed" },
+ { 0, NULL }
+};
+
+struct valinfo smart_logstat[] = {
+ { SMART_LOG_STATE_UNK, "unknown" },
+ { SMART_LOG_STATE_SLEEP, "sleep" },
+ { SMART_LOG_STATE_ACTIDL, "active/idle" },
+ { SMART_LOG_STATE_OFFSELF, "off-line or self-test" },
+ { 0, NULL }
+};
+
+/*
+ * Tables containing values used for reading
+ * device attributes.
+ */
+
+struct valinfo ibm_attr_names[] = {
+ { 1, "Raw Read Error Rate" },
+ { 2, "Throughput Performance" },
+ { 3, "Spin Up Time" },
+ { 4, "Start/Stop Count" },
+ { 5, "Reallocated Sector Count" },
+ { 7, "Seek Error Rate" },
+ { 8, "Seek Time Performance" },
+ { 9, "Power-on Hours Count" },
+ { 10, "Spin Retry Count" },
+ { 12, "Device Power Cycle Count" },
+ { 192, "Power-off Retract Count" },
+ { 193, "Load Cycle Count" },
+ { 194, "Temperature" },
+ { 196, "Reallocation Event Count" },
+ { 197, "Current Pending Sector Count" },
+ { 198, "Off-line Scan Uncorrectable Sector Count" },
+ { 199, "Ultra DMA CRC Error Count" },
+ { 0, NULL },
+};
+
+#define MAKEWORD(b1, b2) \
+ (b2 << 8 | b1)
+#define MAKEDWORD(b1, b2, b3, b4) \
+ (b4 << 24 | b3 << 16 | b2 << 8 | b1)
+
int
main(argc, argv)
int argc;
char *argv[];
{
- int i;
+ struct command *cmdp;
+ char dvname_store[MAXPATHLEN];
- dvname = argv[1];
- if (argc == 2) {
- cmdname = "identify";
- argv += 2;
- argc -= 2;
- } else if (argc < 3) {
+ if (argc < 2)
usage();
- } else {
- /* Skip program name, get and skip device name and command. */
-
- cmdname = argv[2];
- argv += 3;
- argc -= 3;
- }
/*
* Open the device
*/
- fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0);
+ fd = opendisk(argv[1], O_RDWR, dvname_store, sizeof(dvname_store), 0);
if (fd == -1) {
if (errno == ENOENT) {
/*
@@ -223,27 +337,32 @@ main(argc, argv)
* which leaves off the "r" in front of the device's
* name.
*/
- fd = opendisk(dvname, O_RDWR, dvname_store,
+ fd = opendisk(argv[1], O_RDWR, dvname_store,
sizeof(dvname_store), 1);
if (fd == -1)
- err(1, "%s", dvname);
+ err(1, "%s", argv[1]);
} else
- err(1, "%s", dvname);
+ err(1, "%s", argv[1]);
}
- /*
- * Point the dvname at the actual device name that opendisk() opened.
- */
- dvname = dvname_store;
+ /* Skip program name and device name. */
+ if (argc != 2) {
+ argv += 2;
+ argc -= 2;
+ } else {
+ argv[1] = "identify";
+ argv += 1;
+ argc -= 1;
+ }
/* Look up and call the command. */
- for (i = 0; commands[i].cmd_name != NULL; i++)
- if (strcmp(cmdname, commands[i].cmd_name) == 0)
+ for (cmdp = commands; cmdp->cmd_name != NULL; cmdp++)
+ if (strcmp(argv[0], cmdp->cmd_name) == 0)
break;
- if (commands[i].cmd_name == NULL)
- errx(1, "unknown command: %s", cmdname);
+ if (cmdp->cmd_name == NULL)
+ errx(1, "unknown command: %s", argv[0]);
- (*commands[i].cmd_func)(argc, argv);
+ (cmdp->cmd_func)(argc, argv);
return (0);
}
@@ -260,7 +379,6 @@ usage()
/*
* Wrapper that calls ATAIOCCOMMAND and checks for errors
*/
-
void
ata_command(req)
struct atareq *req;
@@ -300,7 +418,6 @@ ata_command(req)
/*
* Print out strings associated with particular bitmasks
*/
-
void
print_bitinfo(f, bits, binfo)
const char *f;
@@ -314,9 +431,40 @@ print_bitinfo(f, bits, binfo)
}
/*
- * DEVICE COMMANDS
+ * strtoval():
+ * returns value associated with given string,
+ * if no value found -1 is returned.
+ */
+int
+strtoval(str, vinfo)
+ const char *str;
+ struct valinfo *vinfo;
+{
+ for (; vinfo->string != NULL; vinfo++)
+ if (strcmp(str, vinfo->string) == 0)
+ return vinfo->value;
+ return -1;
+}
+
+/*
+ * valtostr():
+ * returns string associated with given value,
+ * if no string found NULL is returned.
*/
+const char *
+valtostr(val, vinfo)
+ int val;
+ struct valinfo *vinfo;
+{
+ for (; vinfo->string != NULL; vinfo++)
+ if (val == vinfo->value)
+ return vinfo->string;
+ return NULL;
+}
+/*
+ * DEVICE COMMANDS
+ */
void
device_dump(argc, argv)
int argc;
@@ -349,10 +497,9 @@ device_identify(argc, argv)
{
struct ataparams *inqbuf;
struct atareq req;
- unsigned char inbuf[512];
+ char inbuf[512], *s;
- /* No arguments. */
- if (argc != 0)
+ if (argc != 1)
goto usage;
memset(&inbuf, 0, sizeof(inbuf));
@@ -388,27 +535,20 @@ device_identify(argc, argv)
}
/*
- * Strip blanks off of the info strings. Yuck, I wish this was
- * cleaner.
+ * Strip blanks off of the info strings.
*/
- if (inqbuf->atap_model[sizeof(inqbuf->atap_model) - 1] == ' ') {
- inqbuf->atap_model[sizeof(inqbuf->atap_model) - 1] = '\0';
- while (inqbuf->atap_model[strlen(inqbuf->atap_model) - 1] == ' ')
- inqbuf->atap_model[strlen(inqbuf->atap_model) - 1] = '\0';
- }
+ for (s = &inqbuf->atap_model[sizeof(inqbuf->atap_model) - 1];
+ s >= (char *)inqbuf->atap_model && *s == ' '; s--)
+ *s = '\0';
- if (inqbuf->atap_revision[sizeof(inqbuf->atap_revision) - 1] == ' ') {
- inqbuf->atap_revision[sizeof(inqbuf->atap_revision) - 1] = '\0';
- while (inqbuf->atap_revision[strlen(inqbuf->atap_revision) - 1] == ' ')
- inqbuf->atap_revision[strlen(inqbuf->atap_revision) - 1] = '\0';
- }
+ for (s = &inqbuf->atap_revision[sizeof(inqbuf->atap_revision) - 1];
+ s >= (char *)inqbuf->atap_revision && *s == ' '; s--)
+ *s = '\0';
- if (inqbuf->atap_serial[sizeof(inqbuf->atap_serial) - 1] == ' ') {
- inqbuf->atap_serial[sizeof(inqbuf->atap_serial) - 1] = '\0';
- while (inqbuf->atap_serial[strlen(inqbuf->atap_serial) - 1] == ' ')
- inqbuf->atap_serial[strlen(inqbuf->atap_serial) - 1] = '\0';
- }
+ for (s = &inqbuf->atap_serial[sizeof(inqbuf->atap_serial) - 1];
+ s >= (char *)inqbuf->atap_serial && *s == ' '; s--)
+ *s = '\0';
printf("Model: %.*s, Rev: %.*s, Serial #: %.*s\n",
(int) sizeof(inqbuf->atap_model), inqbuf->atap_model,
@@ -449,25 +589,16 @@ device_identify(argc, argv)
}
if (inqbuf->atap_cmd_def != 0 && inqbuf->atap_cmd_def != 0xffff) {
- printf("Device has enabled the following command sets/features:\n");
+ printf("Device has enabled the following command "
+ "sets/features:\n");
print_bitinfo("\t%s\n", inqbuf->atap_cmd1_en, ata_cmd_set1);
print_bitinfo("\t%s\n", inqbuf->atap_cmd2_en, ata_cmd_set2);
-#if 0
- print_bitinfo("\t%s\n", inqbuf->atap_cmd_set1 &
- (WDC_CMD1_SRV | WDC_CMD1_RLSE | WDC_CMD1_AHEAD |
- WDC_CMD1_CACHE | WDC_CMD1_SEC | WDC_CMD1_SMART),
- ata_cmd_set1);
- print_bitinfo("\t%s\n", inqbuf->atap_cmd_set2 &
- (WDC_CMD2_RMSN | ATA_CMD2_APM | ATAPI_CMD2_PUIS |
- ATAPI_CMD2_AAM | ATAPI_CMD2_48AD |
- ATAPI_CMD2_DCO), ata_cmd_set2);
-#endif
}
return;
usage:
- fprintf(stderr, "usage: %s device %s\n", __progname, cmdname);
+ fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]);
exit(1);
}
@@ -476,7 +607,6 @@ usage:
*
* issue the IDLE IMMEDIATE command to the drive
*/
-
void
device_idle(argc, argv)
int argc;
@@ -484,15 +614,14 @@ device_idle(argc, argv)
{
struct atareq req;
- /* No arguments. */
- if (argc != 0)
+ if (argc != 1)
goto usage;
memset(&req, 0, sizeof(req));
- if (strcmp(cmdname, "idle") == 0)
+ if (strcmp(argv[0], "idle") == 0)
req.command = WDCC_IDLE_IMMED;
- else if (strcmp(cmdname, "standby") == 0)
+ else if (strcmp(argv[0], "standby") == 0)
req.command = WDCC_STANDBY_IMMED;
else
req.command = WDCC_SLEEP;
@@ -503,68 +632,545 @@ device_idle(argc, argv)
return;
usage:
- fprintf(stderr, "usage: %s device %s\n", __progname, cmdname);
+ fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]);
exit(1);
}
/*
- * SMART.
- *
- * issue the SMART ENABLE/DISABLE/STATUS commands to the drive
+ * SMART ENABLE OPERATIONS command
+ */
+void
+device_smart_enable(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct atareq req;
+
+ if (argc != 1)
+ goto usage;
+
+ memset(&req, 0, sizeof(req));
+
+ req.command = ATAPI_SMART;
+ req.cylinder = 0xc24f;
+ req.timeout = 1000;
+ req.features = ATAPI_SMART_EN;
+
+ ata_command(&req);
+
+ return;
+usage:
+ fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]);
+ exit(1);
+}
+
+/*
+ * SMART DISABLE OPERATIONS command
+ */
+void
+device_smart_disable(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct atareq req;
+
+ if (argc != 1)
+ goto usage;
+
+ memset(&req, 0, sizeof(req));
+
+ req.command = ATAPI_SMART;
+ req.cylinder = 0xc24f;
+ req.timeout = 1000;
+ req.features = ATAPI_SMART_DS;
+
+ ata_command(&req);
+
+ return;
+usage:
+ fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]);
+ exit(1);
+}
+
+/*
+ * SMART STATUS command
+ */
+void
+device_smart_status(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct atareq req;
+
+ if (argc != 1)
+ goto usage;
+
+ memset(&req, 0, sizeof(req));
+
+ req.command = ATAPI_SMART;
+ req.cylinder = 0xc24f;
+ req.timeout = 1000;
+ req.features = ATAPI_SMART_STATUS;
+
+ ata_command(&req);
+
+ if (req.cylinder == 0xc24f)
+ printf("No SMART threshold exceeded\n");
+ else if (req.cylinder == 0x2cf4) {
+ fprintf(stderr,"SMART threshold exceeded!\n");
+ exit(2);
+ } else {
+ fprintf(stderr, "Unknown response %02x!\n", req.cylinder);
+ exit(1);
+ }
+
+ return;
+usage:
+ fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]);
+ exit(1);
+}
+
+/*
+ * SMART ENABLE/DISABLE ATTRIBUTE AUTOSAVE command
+ */
+void
+device_smart_autosave(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct atareq req;
+ int val;
+
+ if (argc != 2)
+ goto usage;
+
+ memset(&req, 0, sizeof(req));
+
+ req.command = ATAPI_SMART;
+ req.cylinder = 0xc24f;
+ req.timeout = 1000;
+ req.features = ATAPI_SMART_AUTOSAVE;
+ if ((val = strtoval(argv[1], smart_autosave)) == -1)
+ goto usage;
+ req.sec_num = val;
+
+ ata_command(&req);
+
+ return;
+usage:
+ fprintf(stderr, "usage: %s device %s enable | disable\n", __progname,
+ argv[0]);
+ exit(1);
+}
+
+/*
+ * SMART EXECUTE OFF-LINE IMMEDIATE command
*/
+void
+device_smart_offline(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct atareq req;
+ int val;
+
+ if (argc != 2)
+ goto usage;
+
+ memset(&req, 0, sizeof(req));
+
+ req.command = ATAPI_SMART;
+ req.cylinder = 0xc24f;
+ req.timeout = 1000;
+ req.features = ATAPI_SMART_OFFLINE;
+ if ((val = strtoval(argv[1], smart_offline)) == -1)
+ goto usage;
+ req.sec_num = val;
+ ata_command(&req);
+
+ return;
+usage:
+ fprintf(stderr, "usage: %s device %s subcommand\n", __progname,
+ argv[0]);
+ exit(1);
+}
+
+/*
+ * SMART READ DATA command
+ */
void
-device_smart(argc, argv)
+device_smart_read(argc, argv)
int argc;
char *argv[];
{
struct atareq req;
+ struct smart_read data;
- /* No arguments. */
- if (argc != 0)
+ if (argc != 1)
goto usage;
memset(&req, 0, sizeof(req));
+ memset(&data, 0, sizeof(data));
req.command = ATAPI_SMART;
- req.cylinder = 0xC24F; /* Cylinders is mapped to LBA Mid/Low */
- /* XXX: I assume cylinders is correctly mapped w.r.t.
- * endianness? */
-
- if (strcmp(cmdname, "smartenable") == 0)
- req.features = ATAPI_SMART_EN;
- else if (strcmp(cmdname, "smartdisable") == 0)
- req.features = ATAPI_SMART_DS;
- else if (strcmp(cmdname, "smartstatus") == 0)
- req.features = ATAPI_SMART_STATUS;
+ req.cylinder = 0xc24f;
+ req.timeout = 1000;
+ req.features = ATAPI_SMART_READ;
+ req.flags = ATACMD_READ;
+ req.databuf = (caddr_t)&data;
+ req.datalen = sizeof(data);
+
+ ata_command(&req);
+
+ if (smart_cksum((u_int8_t *)&data, sizeof(data)) != 0) {
+ fprintf(stderr, "Checksum mismatch\n");
+ exit(1);
+ }
+
+ printf("Off-line data collection:\n");
+ printf(" status: %s\n",
+ valtostr(data.offstat & 0x7f, smart_offstat));
+ printf(" activity completion time: %d seconds\n",
+ letoh16(data.time));
+ printf(" capabilities:\n");
+ print_bitinfo("\t%s\n", data.offcap, smart_offcap);
+ printf("Self-test execution:\n");
+ printf(" status: %s\n", valtostr(SMART_SELFSTAT_STAT(data.selfstat),
+ smart_selfstat));
+ if (SMART_SELFSTAT_STAT(data.selfstat) == SMART_SELFSTAT_PROGRESS)
+ printf("remains %d%% of total time\n",
+ SMART_SELFSTAT_PCNT(data.selfstat));
+ printf(" recommended polling time:\n");
+ printf("\tshort routine: %d minutes\n", data.shtime);
+ printf("\textended routine: %d minutes\n", data.extime);
+ printf("SMART capabilities:\n");
+ print_bitinfo(" %s\n", letoh16(data.smartcap), smart_smartcap);
+ printf("Error logging: ");
+ if (data.errcap & SMART_ERRCAP_ERRLOG)
+ printf("supported\n");
else
+ printf("not supported\n");
+
+ return;
+usage:
+ fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]);
+ exit(1);
+}
+
+/*
+ * SMART READ LOG command
+ */
+void
+device_smart_readlog(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct atareq req;
+ int val;
+ u_int8_t inbuf[DEV_BSIZE];
+
+ if (argc != 2)
goto usage;
+ memset(&req, 0, sizeof(req));
+ memset(&inbuf, 0, sizeof(inbuf));
+
+ req.command = ATAPI_SMART;
+ req.cylinder = 0xc24f;
req.timeout = 1000;
+ req.features = ATAPI_SMART_READLOG;
+ req.flags = ATACMD_READ;
+ req.sec_count = 1;
+ req.databuf = (caddr_t)inbuf;
+ req.datalen = sizeof(inbuf);
+ if ((val = strtoval(argv[1], smart_readlog)) == -1)
+ goto usage;
+ req.sec_num = val;
ata_command(&req);
- if (strcmp(cmdname, "smartstatus") == 0) {
- if (req.cylinder == 0xC24F)
- printf("No SMART threshold exceeded\n");
- else if (req.cylinder == 0x2CF4) {
- fprintf(stderr,"SMART threshold exceeded!\n");
- exit(2);
- } else {
- fprintf(stderr, "Unknown response %02x!\n",
- req.cylinder);
+ if (strcmp(argv[1], "directory") == 0) {
+ struct smart_log_dir *data = (struct smart_log_dir *)inbuf;
+ int i;
+
+ if (data->version != SMART_LOG_MSECT) {
+ printf("Device doesn't support multi-sector logs\n");
+ return;
+ }
+
+ for (i = 0; i < 255; i++)
+ printf("Log address %d: %d sectors\n", i + 1,
+ data->entry[i].sec_num);
+ } else if (strcmp(argv[1], "summary") == 0) {
+ struct smart_log_sum *data = (struct smart_log_sum *)inbuf;
+ int i, n, nerr;
+
+ if (smart_cksum(inbuf, sizeof(inbuf)) != 0) {
+ fprintf(stderr, "Checksum mismatch\n");
+ exit(1);
+ }
+
+ if (data->index == 0) {
+ printf("No log entries\n");
+ return;
+ }
+
+ nerr = letoh16(data->err_cnt);
+ printf("Error count: %d\n\n", nerr);
+ /*
+ * Five error log data structures form a circular
+ * buffer. data->index points to the most recent
+ * record and err_cnt contains total error number.
+ * We pass from the most recent record to the
+ * latest one.
+ */
+ i = data->index - 1;
+ n = 0;
+ do {
+ printf("Error %d:\n", n + 1);
+ smart_print_errdata(&data->errdata[i--]);
+ if (i == -1)
+ i = 4;
+ } while (++n < (nerr > 5 ? 5 : nerr));
+ } else if (strcmp(argv[1], "comp") == 0) {
+ struct smart_log_comp *data = (struct smart_log_comp *)inbuf;
+ u_int8_t *newbuf;
+ int i, n, nerr, nsect;
+
+ if (smart_cksum(inbuf, sizeof(inbuf)) != 0) {
+ fprintf(stderr, "Checksum mismatch\n");
+ exit(1);
+ }
+
+ if (data->index == 0) {
+ printf("No log entries\n");
+ return;
+ }
+
+ i = data->index - 1;
+ nerr = letoh16(data->err_cnt);
+ printf("Error count: %d\n", nerr);
+ /*
+ * From the first sector we obtain total error number
+ * and calculate necessary number of sectors to read.
+ * All read error data structures form a circular
+ * buffer and we pass from the most recent record to
+ * the latest one.
+ */
+ nsect = nerr / 5 + (nerr % 5 != 0 ? 1 : 0);
+ if ((newbuf = (u_int8_t *)malloc(nsect * DEV_BSIZE)) == NULL)
+ err(1, "malloc()");
+ memset(&req, 0, sizeof(req));
+ req.flags = ATACMD_READ;
+ req.command = ATAPI_SMART;
+ req.features = ATAPI_SMART_READLOG;
+ req.sec_count = nsect;
+ req.sec_num = SMART_READLOG_COMP;
+ req.cylinder = 0xc24f;
+ req.databuf = (caddr_t)newbuf;
+ req.datalen = nsect * DEV_BSIZE;
+ req.timeout = 1000;
+ ata_command(&req);
+
+ n = 0;
+ data = (struct smart_log_comp *)
+ (newbuf + (nsect - 1) * DEV_BSIZE);
+ do {
+ printf("Error %d:\n", n + 1);
+ smart_print_errdata(&data->errdata[i-- % 5]);
+ if (i == -1)
+ i = 254;
+ if (i % 5 == 4)
+ data = (struct smart_log_comp *)
+ (newbuf + (i / 5) * DEV_BSIZE);
+ } while (++n < nerr);
+ } else if (strcmp(argv[1], "selftest") == 0) {
+ struct smart_log_self *data = (struct smart_log_self *)inbuf;
+ int i, n;
+
+ if (smart_cksum(inbuf, sizeof(inbuf)) != 0) {
+ fprintf(stderr, "Checksum mismatch\n");
exit(1);
}
+
+ if (data->index == 0) {
+ printf("No log entries\n");
+ return;
+ }
+
+ /* circular buffer of 21 entries */
+ i = data->index - 1;
+ n = 0;
+ do {
+ /* don't print empty entries */
+ if ((data->desc[i].time1 | data->desc[i].time2) == 0)
+ break;
+ printf("Test %d\n", n + 1);
+ printf(" LBA Low: 0x%x\n", data->desc[i].reg_lbalo);
+ printf(" status: %s\n",
+ valtostr(SMART_SELFSTAT_STAT(
+ data->desc[i].selfstat),
+ smart_selfstat));
+ printf(" timestamp: %d\n",
+ MAKEWORD(data->desc[i].time1,
+ data->desc[i].time2));
+ printf(" failure checkpoint byte: 0x%x\n",
+ data->desc[i].chkpnt);
+ printf(" failing LBA: 0x%x\n",
+ MAKEDWORD(data->desc[i].lbafail1,
+ data->desc[i].lbafail2,
+ data->desc[i].lbafail3,
+ data->desc[i].lbafail4));
+ if (--i == -1)
+ i = 20;
+ } while (++n < 21);
}
return;
usage:
- fprintf(stderr, "usage: %s device %s\n", __progname, cmdname);
+ fprintf(stderr, "usage: %s device %s log\n", __progname, argv[0]);
exit(1);
}
+#define SMART_PRINTREG(str, reg) \
+ printf(str "0x%02x\t0x%02x\t0x%02x\t0x%02x\t0x%02x\n", \
+ data->cmd[0].reg, \
+ data->cmd[1].reg, \
+ data->cmd[2].reg, \
+ data->cmd[3].reg, \
+ data->cmd[4].reg)
+
+void
+smart_print_errdata(data)
+ struct smart_log_errdata *data;
+{
+ printf(" error register: 0x%x\n", data->err.reg_err);
+ printf(" sector count register: 0x%x\n", data->err.reg_seccnt);
+ printf(" LBA Low register: 0x%x\n", data->err.reg_lbalo);
+ printf(" LBA Mid register: 0x%x\n", data->err.reg_lbamid);
+ printf(" LBA High register: 0x%x\n", data->err.reg_lbahi);
+ printf(" device register: 0x%x\n", data->err.reg_dev);
+ printf(" status register: 0x%x\n", data->err.reg_stat);
+ printf(" state: %s\n", valtostr(data->err.state, smart_logstat));
+ printf(" timestamp: %d\n", MAKEWORD(data->err.time1,
+ data->err.time2));
+ printf(" history:\n");
+ SMART_PRINTREG("\tcontrol register:\t", reg_ctl);
+ SMART_PRINTREG("\tfeatures register:\t", reg_feat);
+ SMART_PRINTREG("\tsector count register:\t", reg_seccnt);
+ SMART_PRINTREG("\tLBA Low register:\t", reg_lbalo);
+ SMART_PRINTREG("\tLBA Mid register:\t", reg_lbamid);
+ SMART_PRINTREG("\tLBA High register:\t", reg_lbahi);
+ SMART_PRINTREG("\tdevice register:\t", reg_dev);
+ SMART_PRINTREG("\tcommand register:\t", reg_cmd);
+ printf("\ttimestamp:\t\t"
+ "%d\t%d\t%d\t%d\t%d\n",
+ MAKEDWORD(data->cmd[0].time1, data->cmd[0].time2,
+ data->cmd[0].time3, data->cmd[0].time4),
+ MAKEDWORD(data->cmd[1].time1, data->cmd[1].time2,
+ data->cmd[1].time3, data->cmd[1].time4),
+ MAKEDWORD(data->cmd[2].time1, data->cmd[2].time2,
+ data->cmd[2].time3, data->cmd[2].time4),
+ MAKEDWORD(data->cmd[3].time1, data->cmd[3].time2,
+ data->cmd[3].time3, data->cmd[3].time4),
+ MAKEDWORD(data->cmd[4].time1, data->cmd[4].time2,
+ data->cmd[4].time3, data->cmd[4].time4));
+}
+
+int
+smart_cksum(data, len)
+ u_int8_t *data;
+ int len;
+{
+ u_int8_t sum = 0;
+ int i;
+
+ for (i = 0; i < len; i++)
+ sum += data[i];
+
+ return sum;
+}
+
/*
- * Set the automatic acoustic managmement on the disk.
+ * Read device attributes
+ */
+void
+device_attr(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct atareq req;
+ struct smart_read attr_val;
+ struct smart_threshold attr_thr;
+ struct attribute *attr;
+ struct threshold *thr;
+ const char *attr_name;
+ static const char hex[]="0123456789abcdef";
+ char raw[13], *format;
+ int i, k, threshold_exceeded = 0;
+
+ memset(&req, 0, sizeof(req));
+ memset(&attr_val, 0, sizeof(attr_val)); /* XXX */
+ memset(&attr_thr, 0, sizeof(attr_thr)); /* XXX */
+
+ req.command = ATAPI_SMART;
+ req.cylinder = 0xc24f; /* LBA High = C2h, LBA Mid = 4Fh */
+ req.timeout = 1000;
+
+ req.features = ATAPI_SMART_READ;
+ req.flags = ATACMD_READ;
+ req.databuf = (caddr_t)&attr_val;
+ req.datalen = sizeof(attr_val);
+ ata_command(&req);
+
+ req.features = SMART_THRESHOLD;
+ req.flags = ATACMD_READ;
+ req.databuf = (caddr_t)&attr_thr;
+ req.datalen = sizeof(attr_thr);
+ ata_command(&req);
+
+ if (attr_val.revision != attr_thr.revision) {
+ /*
+ * Non standard vendor implementation.
+ * Return, since we don't know how to use this.
+ */
+ return;
+ }
+ printf("Attributes table revision: %d\n", attr_val.revision);
+
+ attr = attr_val.attribute;
+ thr = attr_thr.threshold;
+ printf("ID\tAttribute name\t\t\tThreshold\tValue\tRaw\n");
+ for (i = 0; i < 30; i++) {
+ if (thr[i].id != 0 && thr[i].id == attr[i].id) {
+ attr_name = valtostr(thr[i].id, ibm_attr_names);
+ if (attr_name == NULL)
+ attr_name = "Unknown";
+
+ for (k = 0; k < 6; k++) {
+ u_int8_t b;
+ b = attr[i].raw[6 - k];
+ raw[k + k] = hex[b >> 4];
+ raw[k + k + 1] = hex[b & 0x0f];
+ }
+ raw[k + k] = '\0';
+ if (thr[i].value >= attr[i].value) {
+ ++threshold_exceeded;
+ format = "%3d *%-32.32s %3d\t\t%3d\t0x%s\n";
+ } else {
+ format = "%3d\t%-32.32s %3d\t\t%3d\t0x%s\n";
+ }
+ printf(format, thr[i].id, attr_name,
+ thr[i].value, attr[i].value, raw);
+ }
+ }
+ if (threshold_exceeded)
+ fprintf(stderr, "One or more threshold values exceeded!\n");
+}
+
+/*
+ * Set the automatic acoustic management on the disk.
*/
void
device_acoustic(argc, argv)
@@ -575,15 +1181,14 @@ device_acoustic(argc, argv)
struct atareq req;
char *end;
- /* Only one argument */
- if (argc != 1)
+ if (argc != 2)
goto usage;
- acoustic = strtoul(argv[0], &end, 0);
+ acoustic = strtoul(argv[1], &end, 0);
if (*end != '\0') {
fprintf(stderr, "Invalid acoustic management value: \"%s\""
- "(valid values range from 0 to 126)\n", argv[0]);
+ "(valid values range from 0 to 126)\n", argv[1]);
exit(1);
}
@@ -606,8 +1211,8 @@ device_acoustic(argc, argv)
return;
usage:
- fprintf(stderr, "usage; %s device %s acoustic-management-value\n",
- __progname, cmdname);
+ fprintf(stderr, "usage: %s device %s acoustic-management-value\n",
+ __progname, argv[0]);
exit(1);
}
@@ -625,16 +1230,15 @@ device_apm(argc, argv)
struct atareq req;
char *end;
- /* Only one argument */
- if (argc != 1)
+ if (argc != 2)
goto usage;
- power = strtoul(argv[0], &end, 0);
+ power = strtoul(argv[1], &end, 0);
if (*end != '\0') {
fprintf(stderr, "Invalid advanced power management value: "
"\"%s\" (valid values range from 0 to 253)\n",
- argv[0]);
+ argv[1]);
exit(1);
}
@@ -657,8 +1261,8 @@ device_apm(argc, argv)
return;
usage:
- fprintf(stderr, "usage; %s device %s power-management-level\n",
- __progname, cmdname);
+ fprintf(stderr, "usage: %s device %s power-management-level\n",
+ __progname, argv[0]);
exit(1);
}
@@ -673,31 +1277,30 @@ device_feature(argc, argv)
{
struct atareq req;
- /* No argument */
- if (argc != 0)
+ if (argc != 1)
goto usage;
memset(&req, 0, sizeof(req));
req.command = SET_FEATURES ;
- if (strcmp(cmdname, "acousticdisable") == 0)
+ if (strcmp(argv[0], "acousticdisable") == 0)
req.features = WDSF_AAM_DS;
- else if (strcmp(cmdname, "readaheadenable") == 0)
+ else if (strcmp(argv[0], "readaheadenable") == 0)
req.features = WDSF_READAHEAD_EN;
- else if (strcmp(cmdname, "readaheaddisable") == 0)
+ else if (strcmp(argv[0], "readaheaddisable") == 0)
req.features = WDSF_READAHEAD_DS;
- else if (strcmp(cmdname, "writecacheenable") == 0)
+ else if (strcmp(argv[0], "writecacheenable") == 0)
req.features = WDSF_EN_WR_CACHE;
- else if (strcmp(cmdname, "writecachedisable") == 0)
+ else if (strcmp(argv[0], "writecachedisable") == 0)
req.features = WDSF_WRITE_CACHE_DS;
- else if (strcmp(cmdname, "apmdisable") == 0)
+ else if (strcmp(argv[0], "apmdisable") == 0)
req.features = WDSF_APM_DS;
- else if (strcmp(cmdname, "puisenable") == 0)
+ else if (strcmp(argv[0], "puisenable") == 0)
req.features = WDSF_PUIS_EN;
- else if (strcmp(cmdname, "puisdisable") == 0)
+ else if (strcmp(argv[0], "puisdisable") == 0)
req.features = WDSF_PUIS_DS;
- else if (strcmp(cmdname, "puisspinup") == 0)
+ else if (strcmp(argv[0], "puisspinup") == 0)
req.features = WDSF_PUIS_SPINUP;
else
goto usage;
@@ -709,8 +1312,8 @@ device_feature(argc, argv)
return;
usage:
- fprintf(stderr, "usage; %s device %s\n", __progname,
- cmdname);
+ fprintf(stderr, "usage: %s device %s\n", __progname,
+ argv[0]);
exit(1);
}
@@ -718,7 +1321,6 @@ usage:
* Set the idle timer on the disk. Set it for either idle mode or
* standby mode, depending on how we were invoked.
*/
-
void
device_setidle(argc, argv)
int argc;
@@ -728,14 +1330,13 @@ device_setidle(argc, argv)
struct atareq req;
char *end;
- /* Only one argument */
- if (argc != 1)
+ if (argc != 2)
goto usage;
- idle = strtoul(argv[0], &end, 0);
+ idle = strtoul(argv[1], &end, 0);
if (*end != '\0') {
- fprintf(stderr, "Invalid idle time: \"%s\"\n", argv[0]);
+ fprintf(stderr, "Invalid idle time: \"%s\"\n", argv[1]);
exit(1);
}
@@ -752,12 +1353,17 @@ device_setidle(argc, argv)
memset(&req, 0, sizeof(req));
- if (idle <= 240*5)
+ if (idle <= 240 * 5)
req.sec_count = idle / 5;
else
- req.sec_count = idle / (30*60) + 240;
+ req.sec_count = idle / (30 * 60) + 240;
- req.command = cmdname[3] == 's' ? WDCC_STANDBY : WDCC_IDLE;
+ if (strcmp(argv[0], "setstandby") == 0)
+ req.command = WDCC_STANDBY;
+ else if (strcmp(argv[0], "setidle") == 0)
+ req.command = WDCC_IDLE;
+ else
+ goto usage;
req.timeout = 1000;
ata_command(&req);
@@ -765,15 +1371,14 @@ device_setidle(argc, argv)
return;
usage:
- fprintf(stderr, "usage; %s device %s idle-time\n", __progname,
- cmdname);
+ fprintf(stderr, "usage: %s device %s idle-time\n", __progname,
+ argv[0]);
exit(1);
}
/*
* Query the device for the current power mode
*/
-
void
device_checkpower(argc, argv)
int argc;
@@ -781,8 +1386,7 @@ device_checkpower(argc, argv)
{
struct atareq req;
- /* No arguments. */
- if (argc != 0)
+ if (argc != 1)
goto usage;
memset(&req, 0, sizeof(req));
@@ -811,6 +1415,6 @@ device_checkpower(argc, argv)
return;
usage:
- fprintf(stderr, "usage: %s device %s\n", __progname, cmdname);
+ fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]);
exit(1);
}
diff --git a/sbin/atactl/atasmart.h b/sbin/atactl/atasmart.h
new file mode 100644
index 00000000000..a8207e7747c
--- /dev/null
+++ b/sbin/atactl/atasmart.h
@@ -0,0 +1,217 @@
+/* $OpenBSD: atasmart.h,v 1.1 2002/06/08 12:50:42 gluk Exp $ */
+
+/*
+ * Copyright (c) 2002 Alexander Yurchenko <grange@rt.mipt.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#define SMART_THRESHOLD 0xd1 /* SMART read threshold */
+
+/* device attribute */
+struct attribute {
+ u_int8_t id; /* Attribute ID */
+ u_int16_t status; /* Status flags */
+ u_int8_t value; /* Attribute value */
+ u_int8_t raw[8]; /* Vendor specific */
+} __attribute__((packed));
+
+/* read data sector */
+struct smart_read {
+ u_int16_t revision; /* Data structure revision */
+ struct attribute attribute[30]; /* Device attribute */
+ u_int8_t offstat; /* Off-line data collection status */
+#define SMART_OFFSTAT_NOTSTART 0x00
+#define SMART_OFFSTAT_COMPLETE 0x02
+#define SMART_OFFSTAT_SUSPEND 0x04
+#define SMART_OFFSTAT_INTR 0x05
+#define SMART_OFFSTAT_ERROR 0x06
+ u_int8_t selfstat; /* Self-test execution status */
+#define SMART_SELFSTAT_COMPLETE 0x00
+#define SMART_SELFSTAT_ABORT 0x01
+#define SMART_SELFSTAT_INTR 0x02
+#define SMART_SELFSTAT_ERROR 0x03
+#define SMART_SELFSTAT_UNKFAIL 0x04
+#define SMART_SELFSTAT_ELFAIL 0x05
+#define SMART_SELFSTAT_SRVFAIL 0x06
+#define SMART_SELFSTAT_RDFAIL 0x07
+#define SMART_SELFSTAT_PROGRESS 0x0f
+ u_int16_t time; /* Time to complete data collection activity */
+ u_int8_t vendor1; /* Vendor specific */
+ u_int8_t offcap; /* Off-line data collection capability */
+#define SMART_OFFCAP_EXEC 0x01
+#define SMART_OFFCAP_ABORT 0x04
+#define SMART_OFFCAP_READSCAN 0x08
+#define SMART_OFFCAP_SELFTEST 0x10
+ u_int16_t smartcap; /* SMART capability */
+#define SMART_SMARTCAP_SAVE 0x01
+#define SMART_SMARTCAP_AUTOSAVE 0x02
+ u_int8_t errcap; /* Error logging capability */
+#define SMART_ERRCAP_ERRLOG 0x01
+ u_int8_t vendor2; /* Vendor specific */
+ u_int8_t shtime; /* Short self-test polling time */
+ u_int8_t extime; /* Extended self-test polling time */
+ u_int8_t res[12]; /* Reserved */
+ u_int8_t vendor3[125]; /* Vendor specific */
+ u_int8_t cksum; /* Data structure checksum */
+};
+
+/* threshold entry */
+struct threshold {
+ u_int8_t id; /* Threshold ID */
+ u_int8_t value; /* Threshold value */
+ u_int8_t reserve[10];
+};
+
+/* read thresholds sector */
+struct smart_threshold {
+ u_int16_t revision; /* Data structure revision */
+ struct threshold threshold[30];
+ u_int8_t reserve[18];
+ u_int8_t vendor[131];
+ u_int8_t cksum; /* Data structure checksum */
+};
+
+/* log directory entry */
+struct smart_log_dir_entry {
+ u_int8_t sec_num;
+ u_int8_t res;
+};
+
+/* log directory sector */
+struct smart_log_dir {
+ u_int16_t version;
+ struct smart_log_dir_entry entry[255];
+};
+
+/* command data structure */
+struct smart_log_cmd {
+ u_int8_t reg_ctl;
+ u_int8_t reg_feat;
+ u_int8_t reg_seccnt;
+ u_int8_t reg_lbalo;
+ u_int8_t reg_lbamid;
+ u_int8_t reg_lbahi;
+ u_int8_t reg_dev;
+ u_int8_t reg_cmd;
+ u_int8_t time1;
+ u_int8_t time2;
+ u_int8_t time3;
+ u_int8_t time4;
+};
+
+/* error data structure */
+struct smart_log_err {
+ u_int8_t res;
+ u_int8_t reg_err;
+ u_int8_t reg_seccnt;
+ u_int8_t reg_lbalo;
+ u_int8_t reg_lbamid;
+ u_int8_t reg_lbahi;
+ u_int8_t reg_dev;
+ u_int8_t reg_stat;
+ u_int8_t ext[19];
+ u_int8_t state;
+#define SMART_LOG_STATE_UNK 0x0
+#define SMART_LOG_STATE_SLEEP 0x1
+#define SMART_LOG_STATE_ACTIDL 0x2
+#define SMART_LOG_STATE_OFFSELF 0x3
+ u_int8_t time1;
+ u_int8_t time2;
+};
+
+/* error log data structure */
+struct smart_log_errdata {
+ struct smart_log_cmd cmd[5];
+ struct smart_log_err err;
+};
+
+/* summary error log sector */
+struct smart_log_sum {
+ u_int8_t version;
+ u_int8_t index;
+ struct smart_log_errdata errdata[5];
+ u_int16_t err_cnt;
+ u_int8_t res[57];
+ u_int8_t cksum;
+};
+
+/* comprehensive error log sector */
+struct smart_log_comp {
+ u_int8_t version;
+ u_int8_t index;
+ struct smart_log_errdata errdata[5];
+ u_int16_t err_cnt;
+ u_int8_t res[57];
+ u_int8_t cksum;
+};
+
+/* self-test descriptor entry */
+struct smart_log_self_desc {
+ u_int8_t reg_lbalo;
+ u_int8_t selfstat;
+ u_int8_t time1;
+ u_int8_t time2;
+ u_int8_t chkpnt;
+ u_int8_t lbafail1;
+ u_int8_t lbafail2;
+ u_int8_t lbafail3;
+ u_int8_t lbafail4;
+ u_int8_t vendor[15];
+};
+
+/* self-test log sector */
+struct smart_log_self {
+ u_int16_t rev;
+ struct smart_log_self_desc desc[21];
+ u_int8_t vendor[2];
+ u_int8_t index;
+ u_int8_t res[2];
+ u_int8_t cksum;
+};
+
+#define SMART_SELFSTAT_PCNT(s) ((s & 0x0f) * 10)
+#define SMART_SELFSTAT_STAT(s) (s >> 4)
+
+#define SMART_OFFLINE_COLLECT 0
+#define SMART_OFFLINE_SHORTOFF 1
+#define SMART_OFFLINE_EXTENOFF 2
+#define SMART_OFFLINE_ABORT 127
+#define SMART_OFFLINE_SHORTCAP 129
+#define SMART_OFFLINE_EXTENCAP 130
+
+#define SMART_AUTOSAVE_EN 0xf1
+#define SMART_AUTOSAVE_DS 0x00
+
+#define SMART_READLOG_DIR 0x00
+#define SMART_READLOG_SUM 0x01
+#define SMART_READLOG_COMP 0x02
+#define SMART_READLOG_ECOMP 0x03
+#define SMART_READLOG_SELF 0x06
+#define SMART_READLOG_ESELF 0x07
+
+#define SMART_LOG_MSECT 0x01