diff options
Diffstat (limited to 'sbin/atactl/atactl.c')
-rw-r--r-- | sbin/atactl/atactl.c | 888 |
1 files changed, 746 insertions, 142 deletions
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); } |