diff options
author | Grigoriy Orlov <gluk@cvs.openbsd.org> | 2002-06-08 12:50:43 +0000 |
---|---|---|
committer | Grigoriy Orlov <gluk@cvs.openbsd.org> | 2002-06-08 12:50:43 +0000 |
commit | d4eeb0898268fefbf954dfb6408554c51628ff52 (patch) | |
tree | 1c09c99249dee40119a22f0d70e92c62a1754540 /sbin | |
parent | 46d9b4830762b6dab80f47b9f4560feb1dbd97c2 (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.8 | 90 | ||||
-rw-r--r-- | sbin/atactl/atactl.c | 888 | ||||
-rw-r--r-- | sbin/atactl/atasmart.h | 217 |
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 |