summaryrefslogtreecommitdiff
path: root/sbin/atactl/atactl.c
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/atactl/atactl.c')
-rw-r--r--sbin/atactl/atactl.c888
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);
}