Home | History | Annotate | Line # | Download | only in atactl
atactl.c revision 1.24
      1  1.24      lha /*	$NetBSD: atactl.c,v 1.24 2003/12/20 20:03:20 lha Exp $	*/
      2   1.1     kenh 
      3   1.1     kenh /*-
      4   1.1     kenh  * Copyright (c) 1998 The NetBSD Foundation, Inc.
      5   1.1     kenh  * All rights reserved.
      6   1.1     kenh  *
      7   1.1     kenh  * This code is derived from software contributed to The NetBSD Foundation
      8   1.1     kenh  * by Ken Hornstein.
      9   1.1     kenh  *
     10   1.1     kenh  * Redistribution and use in source and binary forms, with or without
     11   1.1     kenh  * modification, are permitted provided that the following conditions
     12   1.1     kenh  * are met:
     13   1.1     kenh  * 1. Redistributions of source code must retain the above copyright
     14   1.1     kenh  *    notice, this list of conditions and the following disclaimer.
     15   1.1     kenh  * 2. Redistributions in binary form must reproduce the above copyright
     16   1.1     kenh  *    notice, this list of conditions and the following disclaimer in the
     17   1.1     kenh  *    documentation and/or other materials provided with the distribution.
     18   1.1     kenh  * 3. All advertising materials mentioning features or use of this software
     19   1.1     kenh  *    must display the following acknowledgement:
     20   1.1     kenh  *	This product includes software developed by the NetBSD
     21   1.1     kenh  *	Foundation, Inc. and its contributors.
     22   1.1     kenh  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23   1.1     kenh  *    contributors may be used to endorse or promote products derived
     24   1.1     kenh  *    from this software without specific prior written permission.
     25   1.1     kenh  *
     26   1.1     kenh  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27   1.1     kenh  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28   1.1     kenh  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29   1.1     kenh  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30   1.1     kenh  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31   1.1     kenh  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32   1.1     kenh  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33   1.1     kenh  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34   1.1     kenh  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35   1.1     kenh  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36   1.1     kenh  * POSSIBILITY OF SUCH DAMAGE.
     37   1.1     kenh  */
     38   1.1     kenh 
     39   1.1     kenh /*
     40   1.4    jwise  * atactl(8) - a program to control ATA devices.
     41   1.1     kenh  */
     42  1.21      agc #include <sys/cdefs.h>
     43  1.21      agc 
     44  1.21      agc #ifndef lint
     45  1.24      lha __RCSID("$NetBSD: atactl.c,v 1.24 2003/12/20 20:03:20 lha Exp $");
     46  1.21      agc #endif
     47  1.21      agc 
     48   1.1     kenh 
     49   1.1     kenh #include <sys/param.h>
     50   1.1     kenh #include <sys/ioctl.h>
     51   1.1     kenh #include <err.h>
     52   1.1     kenh #include <errno.h>
     53   1.1     kenh #include <fcntl.h>
     54   1.1     kenh #include <stdio.h>
     55   1.1     kenh #include <stdlib.h>
     56   1.1     kenh #include <string.h>
     57   1.1     kenh #include <unistd.h>
     58   1.1     kenh #include <util.h>
     59   1.1     kenh 
     60   1.1     kenh #include <dev/ata/atareg.h>
     61  1.15    soren #include <dev/ata/atavar.h>
     62   1.1     kenh #include <dev/ic/wdcreg.h>
     63   1.1     kenh #include <sys/ataio.h>
     64   1.1     kenh 
     65   1.1     kenh struct command {
     66   1.1     kenh 	const char *cmd_name;
     67   1.5    soren 	const char *arg_names;
     68  1.13   simonb 	void (*cmd_func)(int, char *[]);
     69   1.1     kenh };
     70   1.1     kenh 
     71   1.1     kenh struct bitinfo {
     72   1.1     kenh 	u_int bitmask;
     73   1.1     kenh 	const char *string;
     74   1.1     kenh };
     75   1.1     kenh 
     76  1.13   simonb int	main(int, char *[]);
     77  1.13   simonb void	usage(void);
     78  1.13   simonb void	ata_command(struct atareq *);
     79  1.13   simonb void	print_bitinfo(const char *, const char *, u_int, struct bitinfo *);
     80  1.24      lha void	print_smart_status(void *, void *);
     81  1.24      lha void	print_selftest_entry(int, struct ata_smart_selftest *);
     82  1.24      lha 
     83  1.24      lha void	print_selftest(void *);
     84  1.24      lha 
     85  1.20  mycroft int	is_smart(void);
     86   1.1     kenh 
     87   1.1     kenh int	fd;				/* file descriptor for device */
     88   1.1     kenh const	char *dvname;			/* device name */
     89   1.1     kenh char	dvname_store[MAXPATHLEN];	/* for opendisk(3) */
     90   1.1     kenh const	char *cmdname;			/* command user issued */
     91   1.5    soren const	char *argnames;			/* helpstring: expected arguments */
     92   1.1     kenh 
     93  1.13   simonb void	device_identify(int, char *[]);
     94  1.13   simonb void	device_setidle(int, char *[]);
     95  1.13   simonb void	device_idle(int, char *[]);
     96  1.13   simonb void	device_checkpower(int, char *[]);
     97  1.15    soren void	device_smart(int, char *[]);
     98   1.1     kenh 
     99  1.24      lha void	smart_temp(struct ata_smart_attr *, int64_t);
    100  1.24      lha 
    101   1.1     kenh struct command commands[] = {
    102   1.5    soren 	{ "identify",	"",			device_identify },
    103   1.5    soren 	{ "setidle",	"idle-timer",		device_setidle },
    104   1.5    soren 	{ "setstandby",	"standby-timer",	device_setidle },
    105   1.5    soren 	{ "idle",	"",			device_idle },
    106   1.5    soren 	{ "standby",	"",			device_idle },
    107   1.5    soren 	{ "sleep",	"",			device_idle },
    108   1.5    soren 	{ "checkpower",	"",			device_checkpower },
    109  1.24      lha 	{ "smart",	"enable|disable|status|selftest-log", device_smart },
    110   1.5    soren 	{ NULL,		NULL,			NULL },
    111   1.1     kenh };
    112   1.1     kenh 
    113   1.1     kenh /*
    114   1.1     kenh  * Tables containing bitmasks used for error reporting and
    115   1.1     kenh  * device identification.
    116   1.1     kenh  */
    117   1.1     kenh 
    118   1.1     kenh struct bitinfo ata_caps[] = {
    119  1.23     yamt 	{ WDC_CAP_DMA, "DMA" },
    120  1.23     yamt 	{ WDC_CAP_LBA, "LBA" },
    121   1.1     kenh 	{ ATA_CAP_STBY, "ATA standby timer values" },
    122   1.1     kenh 	{ WDC_CAP_IORDY, "IORDY operation" },
    123   1.1     kenh 	{ WDC_CAP_IORDY_DSBL, "IORDY disabling" },
    124  1.22     fvdl 	{ 0, NULL },
    125   1.1     kenh };
    126   1.1     kenh 
    127   1.1     kenh struct bitinfo ata_vers[] = {
    128   1.1     kenh 	{ WDC_VER_ATA1,	"ATA-1" },
    129   1.1     kenh 	{ WDC_VER_ATA2,	"ATA-2" },
    130   1.1     kenh 	{ WDC_VER_ATA3,	"ATA-3" },
    131   1.1     kenh 	{ WDC_VER_ATA4,	"ATA-4" },
    132  1.23     yamt 	{ WDC_VER_ATA5,	"ATA-5" },
    133  1.23     yamt 	{ WDC_VER_ATA6,	"ATA-6" },
    134  1.23     yamt 	{ WDC_VER_ATA7,	"ATA-7" },
    135  1.22     fvdl 	{ 0, NULL },
    136   1.1     kenh };
    137   1.1     kenh 
    138   1.1     kenh struct bitinfo ata_cmd_set1[] = {
    139   1.1     kenh 	{ WDC_CMD1_NOP, "NOP command" },
    140   1.1     kenh 	{ WDC_CMD1_RB, "READ BUFFER command" },
    141   1.1     kenh 	{ WDC_CMD1_WB, "WRITE BUFFER command" },
    142   1.1     kenh 	{ WDC_CMD1_HPA, "Host Protected Area feature set" },
    143   1.1     kenh 	{ WDC_CMD1_DVRST, "DEVICE RESET command" },
    144   1.1     kenh 	{ WDC_CMD1_SRV, "SERVICE interrupt" },
    145   1.1     kenh 	{ WDC_CMD1_RLSE, "release interrupt" },
    146   1.1     kenh 	{ WDC_CMD1_AHEAD, "look-ahead" },
    147   1.1     kenh 	{ WDC_CMD1_CACHE, "write cache" },
    148   1.1     kenh 	{ WDC_CMD1_PKT, "PACKET command feature set" },
    149   1.1     kenh 	{ WDC_CMD1_PM, "Power Management feature set" },
    150   1.1     kenh 	{ WDC_CMD1_REMOV, "Removable Media feature set" },
    151   1.1     kenh 	{ WDC_CMD1_SEC, "Security Mode feature set" },
    152   1.1     kenh 	{ WDC_CMD1_SMART, "SMART feature set" },
    153  1.22     fvdl 	{ 0, NULL },
    154   1.1     kenh };
    155   1.1     kenh 
    156   1.1     kenh struct bitinfo ata_cmd_set2[] = {
    157  1.23     yamt 	{ ATA_CMD2_FCE, "FLUSH CACHE EXT command" },
    158  1.23     yamt 	{ WDC_CMD2_FC, "FLUSH CACHE command" },
    159  1.23     yamt 	{ WDC_CMD2_DCO, "Device Configuration Overlay feature set" },
    160  1.23     yamt 	{ ATA_CMD2_LBA48, "48-bit Address feature set" },
    161  1.23     yamt 	{ WDC_CMD2_AAM, "Automatic Acoustic Management feature set" },
    162  1.23     yamt 	{ WDC_CMD2_SM, "SET MAX security extention" },
    163  1.23     yamt 	{ WDC_CMD2_SFREQ, "SET FEATURES required to spin-up after power-up" },
    164  1.23     yamt 	{ WDC_CMD2_PUIS, "Power-Up In Standby feature set" },
    165   1.1     kenh 	{ WDC_CMD2_RMSN, "Removable Media Status Notification feature set" },
    166   1.1     kenh 	{ ATA_CMD2_APM, "Advanced Power Management feature set" },
    167   1.1     kenh 	{ ATA_CMD2_CFA, "CFA feature set" },
    168   1.6    soren 	{ ATA_CMD2_RWQ, "READ/WRITE DMA QUEUED commands" },
    169   1.1     kenh 	{ WDC_CMD2_DM, "DOWNLOAD MICROCODE command" },
    170  1.22     fvdl 	{ 0, NULL },
    171   1.1     kenh };
    172   1.1     kenh 
    173  1.23     yamt struct bitinfo ata_cmd_ext[] = {
    174  1.23     yamt 	{ ATA_CMDE_TLCONT, "Time-limited R/W feature set R/W Continuous mode" },
    175  1.23     yamt 	{ ATA_CMDE_TL, "Time-limited Read/Write" },
    176  1.23     yamt 	{ ATA_CMDE_URGW, "URG bit for WRITE STREAM DMA/PIO" },
    177  1.23     yamt 	{ ATA_CMDE_URGR, "URG bit for READ STREAM DMA/PIO" },
    178  1.23     yamt 	{ ATA_CMDE_WWN, "World Wide name" },
    179  1.23     yamt 	{ ATA_CMDE_WQFE, "WRITE DMA QUEUED FUA EXT command" },
    180  1.23     yamt 	{ ATA_CMDE_WFE, "WRITE DMA/MULTIPLE FUA EXT commands" },
    181  1.23     yamt 	{ ATA_CMDE_GPL, "General Purpose Logging feature set" },
    182  1.23     yamt 	{ ATA_CMDE_STREAM, "Streaming feature set" },
    183  1.23     yamt 	{ ATA_CMDE_MCPTC, "Media Card Pass Through Command feature set" },
    184  1.23     yamt 	{ ATA_CMDE_MS, "Media serial number" },
    185  1.23     yamt 	{ ATA_CMDE_SST, "SMART self-test" },
    186  1.23     yamt 	{ ATA_CMDE_SEL, "SMART error logging" },
    187  1.23     yamt 	{ 0, NULL },
    188  1.23     yamt };
    189  1.23     yamt 
    190  1.17    soren static const struct {
    191  1.17    soren 	const int	id;
    192  1.17    soren 	const char	*name;
    193  1.24      lha 	void (*special)(struct ata_smart_attr *, int64_t);
    194  1.17    soren } smart_attrs[] = {
    195  1.19    soren 	{ 1,		"Raw read error rate" },
    196  1.17    soren 	{ 2,		"Throughput performance" },
    197  1.17    soren 	{ 3,		"Spin-up time" },
    198  1.17    soren 	{ 4,		"Start/stop count" },
    199  1.17    soren 	{ 5,		"Reallocated sector count" },
    200  1.17    soren 	{ 7,		"Seek error rate" },
    201  1.17    soren 	{ 8,		"Seek time performance" },
    202  1.17    soren 	{ 9,		"Power-on hours count" },
    203  1.17    soren 	{ 10,		"Spin retry count" },
    204  1.17    soren 	{ 11,		"Calibration retry count" },
    205  1.17    soren 	{ 12,		"Device power cycle count" },
    206  1.17    soren 	{ 191,		"Gsense error rate" },
    207  1.17    soren 	{ 192,		"Power-off retract count" },
    208  1.17    soren 	{ 193,		"Load cycle count" },
    209  1.24      lha 	{ 194,		"Temperature",			smart_temp},
    210  1.24      lha 	{ 195,		"Hardware ECC Recovered" },
    211  1.17    soren 	{ 196,		"Reallocated event count" },
    212  1.17    soren 	{ 197,		"Current pending sector" },
    213  1.17    soren 	{ 198,		"Offline uncorrectable" },
    214  1.17    soren 	{ 199,		"Ultra DMA CRC error count" },
    215  1.24      lha 	{ 0,		"Unknown" },
    216  1.17    soren };
    217  1.17    soren 
    218   1.1     kenh int
    219  1.13   simonb main(int argc, char *argv[])
    220   1.1     kenh {
    221   1.1     kenh 	int i;
    222   1.1     kenh 
    223   1.1     kenh 	/* Must have at least: device command */
    224   1.1     kenh 	if (argc < 3)
    225   1.1     kenh 		usage();
    226   1.1     kenh 
    227   1.1     kenh 	/* Skip program name, get and skip device name and command. */
    228   1.1     kenh 	dvname = argv[1];
    229   1.1     kenh 	cmdname = argv[2];
    230   1.1     kenh 	argv += 3;
    231   1.1     kenh 	argc -= 3;
    232   1.1     kenh 
    233   1.1     kenh 	/*
    234   1.1     kenh 	 * Open the device
    235   1.1     kenh 	 */
    236   1.1     kenh 	fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0);
    237   1.1     kenh 	if (fd == -1) {
    238   1.1     kenh 		if (errno == ENOENT) {
    239   1.1     kenh 			/*
    240   1.1     kenh 			 * Device doesn't exist.  Probably trying to open
    241   1.1     kenh 			 * a device which doesn't use disk semantics for
    242   1.1     kenh 			 * device name.  Try again, specifying "cooked",
    243   1.1     kenh 			 * which leaves off the "r" in front of the device's
    244   1.1     kenh 			 * name.
    245   1.1     kenh 			 */
    246   1.1     kenh 			fd = opendisk(dvname, O_RDWR, dvname_store,
    247   1.1     kenh 			    sizeof(dvname_store), 1);
    248   1.1     kenh 			if (fd == -1)
    249   1.1     kenh 				err(1, "%s", dvname);
    250   1.4    jwise 		} else
    251   1.4    jwise 			err(1, "%s", dvname);
    252   1.1     kenh 	}
    253   1.1     kenh 
    254   1.1     kenh 	/*
    255   1.1     kenh 	 * Point the dvname at the actual device name that opendisk() opened.
    256   1.1     kenh 	 */
    257   1.1     kenh 	dvname = dvname_store;
    258   1.1     kenh 
    259   1.1     kenh 	/* Look up and call the command. */
    260   1.1     kenh 	for (i = 0; commands[i].cmd_name != NULL; i++)
    261   1.1     kenh 		if (strcmp(cmdname, commands[i].cmd_name) == 0)
    262   1.1     kenh 			break;
    263   1.1     kenh 	if (commands[i].cmd_name == NULL)
    264  1.12       ad 		errx(1, "unknown command: %s", cmdname);
    265   1.1     kenh 
    266   1.5    soren 	argnames = commands[i].arg_names;
    267   1.5    soren 
    268   1.1     kenh 	(*commands[i].cmd_func)(argc, argv);
    269   1.1     kenh 	exit(0);
    270   1.1     kenh }
    271   1.1     kenh 
    272   1.1     kenh void
    273  1.13   simonb usage(void)
    274   1.1     kenh {
    275   1.5    soren 	int i;
    276   1.1     kenh 
    277   1.5    soren 	fprintf(stderr, "Usage: %s device command [arg [...]]\n",
    278  1.11      cgd 	    getprogname());
    279   1.5    soren 
    280   1.5    soren 	fprintf(stderr, "   Available device commands:\n");
    281   1.5    soren 	for (i=0; commands[i].cmd_name != NULL; i++)
    282   1.5    soren 		fprintf(stderr, "\t%s %s\n", commands[i].cmd_name,
    283   1.5    soren 					    commands[i].arg_names);
    284   1.5    soren 
    285   1.1     kenh 	exit(1);
    286   1.1     kenh }
    287   1.1     kenh 
    288   1.1     kenh /*
    289   1.1     kenh  * Wrapper that calls ATAIOCCOMMAND and checks for errors
    290   1.1     kenh  */
    291   1.1     kenh 
    292   1.1     kenh void
    293  1.13   simonb ata_command(struct atareq *req)
    294   1.1     kenh {
    295   1.1     kenh 	int error;
    296   1.1     kenh 
    297   1.1     kenh 	error = ioctl(fd, ATAIOCCOMMAND, req);
    298   1.1     kenh 
    299   1.1     kenh 	if (error == -1)
    300   1.1     kenh 		err(1, "ATAIOCCOMMAND failed");
    301   1.1     kenh 
    302   1.1     kenh 	switch (req->retsts) {
    303   1.1     kenh 
    304   1.1     kenh 	case ATACMD_OK:
    305   1.1     kenh 		return;
    306   1.1     kenh 	case ATACMD_TIMEOUT:
    307   1.1     kenh 		fprintf(stderr, "ATA command timed out\n");
    308   1.1     kenh 		exit(1);
    309   1.1     kenh 	case ATACMD_DF:
    310   1.1     kenh 		fprintf(stderr, "ATA device returned a Device Fault\n");
    311   1.1     kenh 		exit(1);
    312   1.1     kenh 	case ATACMD_ERROR:
    313   1.1     kenh 		if (req->error & WDCE_ABRT)
    314   1.1     kenh 			fprintf(stderr, "ATA device returned Aborted "
    315   1.1     kenh 				"Command\n");
    316   1.1     kenh 		else
    317   1.1     kenh 			fprintf(stderr, "ATA device returned error register "
    318   1.1     kenh 				"%0x\n", req->error);
    319   1.1     kenh 		exit(1);
    320   1.1     kenh 	default:
    321   1.1     kenh 		fprintf(stderr, "ATAIOCCOMMAND returned unknown result code "
    322   1.1     kenh 			"%d\n", req->retsts);
    323   1.1     kenh 		exit(1);
    324   1.1     kenh 	}
    325   1.1     kenh }
    326   1.1     kenh 
    327   1.1     kenh /*
    328   1.1     kenh  * Print out strings associated with particular bitmasks
    329   1.1     kenh  */
    330   1.1     kenh 
    331   1.1     kenh void
    332  1.13   simonb print_bitinfo(const char *bf, const char *af, u_int bits, struct bitinfo *binfo)
    333   1.1     kenh {
    334   1.1     kenh 
    335  1.22     fvdl 	for (; binfo->bitmask != 0; binfo++)
    336   1.1     kenh 		if (bits & binfo->bitmask)
    337  1.10       is 			printf("%s%s%s", bf, binfo->string, af);
    338   1.1     kenh }
    339   1.1     kenh 
    340  1.24      lha 
    341  1.24      lha /*
    342  1.24      lha  * Try to print SMART temperature field
    343  1.24      lha  */
    344  1.24      lha 
    345  1.24      lha void
    346  1.24      lha smart_temp(struct ata_smart_attr *attr, int64_t raw_value)
    347  1.24      lha {
    348  1.24      lha 	printf("\t%d", (int)attr->raw[0]);
    349  1.24      lha 	if (attr->raw[0] != raw_value)
    350  1.24      lha 		printf(" Lifetime max/min %d/%d",
    351  1.24      lha 		    (int)attr->raw[2],
    352  1.24      lha 		    (int)attr->raw[4]);
    353  1.24      lha }
    354  1.24      lha 
    355  1.24      lha 
    356   1.1     kenh /*
    357  1.15    soren  * Print out SMART attribute thresholds and values
    358  1.15    soren  */
    359  1.15    soren 
    360  1.15    soren void
    361  1.15    soren print_smart_status(void *vbuf, void *tbuf)
    362  1.15    soren {
    363  1.15    soren 	struct ata_smart_attributes *value_buf = vbuf;
    364  1.15    soren 	struct ata_smart_thresholds *threshold_buf = tbuf;
    365  1.24      lha 	struct ata_smart_attr *attr;
    366  1.24      lha 	int64_t raw_value;
    367  1.24      lha 	int flags;
    368  1.17    soren 	int i, j;
    369  1.24      lha 	int aid;
    370  1.15    soren 	int8_t checksum;
    371  1.15    soren 
    372  1.15    soren 	for (i = checksum = 0; i < 511; i++)
    373  1.15    soren 		checksum += ((int8_t *) value_buf)[i];
    374  1.15    soren 	checksum *= -1;
    375  1.15    soren 	if (checksum != value_buf->checksum) {
    376  1.15    soren 		fprintf(stderr, "SMART attribute values checksum error\n");
    377  1.15    soren 		return;
    378  1.15    soren 	}
    379  1.15    soren 
    380  1.15    soren 	for (i = checksum = 0; i < 511; i++)
    381  1.15    soren 		checksum += ((int8_t *) threshold_buf)[i];
    382  1.15    soren 	checksum *= -1;
    383  1.15    soren 	if (checksum != threshold_buf->checksum) {
    384  1.15    soren 		fprintf(stderr, "SMART attribute thresholds checksum error\n");
    385  1.15    soren 		return;
    386  1.15    soren 	}
    387  1.15    soren 
    388  1.24      lha 	printf("id value thresh crit collect reliability description\t\t\traw\n");
    389  1.24      lha 	for (i = 0; i < 256; i++) {
    390  1.24      lha 		int thresh = 0;
    391  1.24      lha 
    392  1.24      lha 		attr = NULL;
    393  1.24      lha 
    394  1.24      lha 		for (j = 0; j < 30; j++) {
    395  1.24      lha 			if (value_buf->attributes[j].id == i)
    396  1.24      lha 				attr = &value_buf->attributes[j];
    397  1.24      lha 			if (threshold_buf->thresholds[j].id == i)
    398  1.24      lha 				thresh = threshold_buf->thresholds[j].value;
    399  1.15    soren 	}
    400  1.15    soren 
    401  1.24      lha 		if (thresh && attr == NULL)
    402  1.24      lha 			errx(1, "threshold but not attr %d", i);
    403  1.24      lha 		if (attr == NULL)
    404  1.24      lha 			continue;
    405  1.24      lha 
    406  1.24      lha 		if (attr->value == 0||attr->value == 0xFE||attr->value == 0xFF)
    407  1.24      lha 			continue;
    408  1.24      lha 
    409  1.24      lha 		for (aid = 0;
    410  1.24      lha 		     smart_attrs[aid].id != i && smart_attrs[aid].id != 0;
    411  1.24      lha 		     aid++)
    412  1.24      lha 			;
    413  1.24      lha 
    414  1.24      lha 		flags = attr->flags;
    415  1.24      lha 
    416  1.24      lha 		printf("%3d %3d  %3d     %-3s %-7s %stive    %-24s",
    417  1.24      lha 		    i, attr->value, thresh,
    418  1.24      lha 		    flags & WDSM_ATTR_ADVISORY ? "yes" : "no",
    419  1.24      lha 		    flags & WDSM_ATTR_COLLECTIVE ? "online" : "offline",
    420  1.24      lha 		    attr->value > thresh ? "posi" : "nega",
    421  1.24      lha 		    smart_attrs[aid].name);
    422  1.24      lha 
    423  1.24      lha 		for (j = 0, raw_value = 0; j < 6; j++)
    424  1.24      lha 			raw_value += ((int64_t)attr->raw[j]) << (8*j);
    425  1.24      lha 
    426  1.24      lha 		if (smart_attrs[aid].special)
    427  1.24      lha 			(*smart_attrs[aid].special)(attr, raw_value);
    428  1.24      lha 		printf("\n");
    429  1.15    soren 		}
    430  1.15    soren 	}
    431  1.24      lha 
    432  1.24      lha struct {
    433  1.24      lha 	int number;
    434  1.24      lha 	const char *name;
    435  1.24      lha } selftest_name[] = {
    436  1.24      lha 	{ 0, "Off-line" },
    437  1.24      lha 	{ 1, "Short off-line" },
    438  1.24      lha 	{ 2, "Extended off-line" },
    439  1.24      lha 	{ 127, "Abort off-line test" },
    440  1.24      lha 	{ 129, "Short captive" },
    441  1.24      lha 	{ 130, "Extended captive" },
    442  1.24      lha 	{ 256, "Unknown test" }, /* larger then u_int8_t */
    443  1.24      lha 	{ 0, NULL }
    444  1.24      lha };
    445  1.24      lha 
    446  1.24      lha const char *selftest_status[] = {
    447  1.24      lha 	"No error",
    448  1.24      lha 	"Aborted by the host",
    449  1.24      lha 	"Interruped by the host by reset",
    450  1.24      lha 	"Fatal error or unknown test error",
    451  1.24      lha 	"Unknown test element failed",
    452  1.24      lha 	"Electrical test element failed",
    453  1.24      lha 	"The Servo (and/or seek) test element failed",
    454  1.24      lha 	"Read element of test failed",
    455  1.24      lha 	"Reserved",
    456  1.24      lha 	"Reserved",
    457  1.24      lha 	"Reserved",
    458  1.24      lha 	"Reserved",
    459  1.24      lha 	"Reserved",
    460  1.24      lha 	"Reserved",
    461  1.24      lha 	"Reserved",
    462  1.24      lha 	"Self-test in progress"
    463  1.24      lha };
    464  1.24      lha 
    465  1.24      lha void
    466  1.24      lha print_selftest_entry(int num, struct ata_smart_selftest *le)
    467  1.24      lha {
    468  1.24      lha 	unsigned char *p;
    469  1.24      lha 	int i;
    470  1.24      lha 
    471  1.24      lha 	/* check if all zero */
    472  1.24      lha 	for (p = (void *)le, i = 0; i < sizeof(*le); i++)
    473  1.24      lha 		if (p[i] != 0)
    474  1.24      lha 			break;
    475  1.24      lha 	if (i == sizeof(*le))
    476  1.24      lha 		return;
    477  1.24      lha 
    478  1.24      lha 	printf("Log entry: %d\n", num);
    479  1.24      lha 
    480  1.24      lha 	/* Get test name */
    481  1.24      lha 	for (i = 0; selftest_name[i].name != NULL; i++)
    482  1.24      lha 		if (selftest_name[i].number == le->number)
    483  1.24      lha 			break;
    484  1.24      lha 	if (selftest_name[i].number == 0)
    485  1.24      lha 		i = 255; /* unknown test */
    486  1.24      lha 
    487  1.24      lha 	printf("\tName: %s\n", selftest_name[i].name);
    488  1.24      lha 	printf("\tStatus: %s\n", selftest_status[le->status >> 4]);
    489  1.24      lha 	if (le->status >> 4 == 15)
    490  1.24      lha 		printf("\tPrecent of test remaning: %1d0\n", le->status & 0xf);
    491  1.24      lha 	if (le->status)
    492  1.24      lha 		printf("LBA first error: %d\n", le->lba_first_error);
    493  1.24      lha }
    494  1.24      lha 
    495  1.24      lha void
    496  1.24      lha print_selftest(void *buf)
    497  1.24      lha {
    498  1.24      lha 	struct ata_smart_selftestlog *stlog = buf;
    499  1.24      lha 	int8_t checksum;
    500  1.24      lha 	int i;
    501  1.24      lha 
    502  1.24      lha 	for (i = checksum = 0; i < 511; i++)
    503  1.24      lha 		checksum += ((int8_t *) buf)[i];
    504  1.24      lha   	checksum *= -1;
    505  1.24      lha 	if ((u_int8_t)checksum != stlog->checksum) {
    506  1.24      lha 		fprintf(stderr, "SMART selftest log checksum error\n");
    507  1.24      lha 		return;
    508  1.24      lha 	}
    509  1.24      lha 
    510  1.24      lha 	if (stlog->data_structure_revision != 1) {
    511  1.24      lha 		fprintf(stderr, "Log revision not 1");
    512  1.24      lha 		return;
    513  1.24      lha 	}
    514  1.24      lha 
    515  1.24      lha 	if (stlog->mostrecenttest == 0) {
    516  1.24      lha 		printf("No self-tests have been logged\n");
    517  1.24      lha 		return;
    518  1.24      lha 	}
    519  1.24      lha 
    520  1.24      lha 	if (stlog->mostrecenttest > 22) {
    521  1.24      lha 		fprintf(stderr, "Most recent test is too large\n");
    522  1.24      lha 		return;
    523  1.24      lha 	}
    524  1.24      lha 
    525  1.24      lha 	for (i = stlog->mostrecenttest; i < 22; i++)
    526  1.24      lha 		print_selftest_entry(i, &stlog->log_entries[i]);
    527  1.24      lha 	for (i = 0; i < stlog->mostrecenttest; i++)
    528  1.24      lha 		print_selftest_entry(i, &stlog->log_entries[i]);
    529  1.15    soren }
    530  1.15    soren 
    531  1.15    soren /*
    532  1.15    soren  * is_smart:
    533  1.15    soren  *
    534  1.15    soren  *	Detect whether device supports SMART and SMART is enabled.
    535  1.15    soren  */
    536  1.15    soren 
    537  1.15    soren int
    538  1.20  mycroft is_smart(void)
    539  1.15    soren {
    540  1.15    soren 	int retval = 0;
    541  1.15    soren 	struct atareq req;
    542  1.15    soren 	unsigned char inbuf[DEV_BSIZE];
    543  1.15    soren 	struct ataparams *inqbuf;
    544  1.15    soren 	char *status;
    545  1.15    soren 
    546  1.15    soren 	memset(&inbuf, 0, sizeof(inbuf));
    547  1.15    soren 	memset(&req, 0, sizeof(req));
    548  1.15    soren 
    549  1.15    soren 	inqbuf = (struct ataparams *) inbuf;
    550  1.15    soren 
    551  1.15    soren 	req.flags = ATACMD_READ;
    552  1.15    soren 	req.command = WDCC_IDENTIFY;
    553  1.15    soren 	req.databuf = (caddr_t) inbuf;
    554  1.15    soren 	req.datalen = sizeof(inbuf);
    555  1.15    soren 	req.timeout = 1000;
    556  1.15    soren 
    557  1.15    soren 	ata_command(&req);
    558  1.15    soren 
    559  1.15    soren 	if (inqbuf->atap_cmd_def != 0 && inqbuf->atap_cmd_def != 0xffff) {
    560  1.15    soren 		if (!(inqbuf->atap_cmd_set1 & WDC_CMD1_SMART)) {
    561  1.15    soren 			fprintf(stderr, "SMART unsupported\n");
    562  1.15    soren 		} else {
    563  1.15    soren 			if (inqbuf->atap_ata_major <= WDC_VER_ATA5 ||
    564  1.15    soren 			    inqbuf->atap_cmd_set2 == 0xffff ||
    565  1.15    soren 			    inqbuf->atap_cmd_set2 == 0x0000) {
    566  1.15    soren 				status = "status unknown";
    567  1.15    soren 				retval = 2;
    568  1.15    soren 			} else {
    569  1.18  mycroft 				if (inqbuf->atap_cmd1_en & WDC_CMD1_SMART) {
    570  1.15    soren 					status = "enabled";
    571  1.15    soren 					retval = 1;
    572  1.15    soren 				} else {
    573  1.15    soren 					status = "disabled";
    574  1.15    soren 				}
    575  1.15    soren 			}
    576  1.20  mycroft 			printf("SMART supported, SMART %s\n", status);
    577  1.15    soren 		}
    578  1.15    soren 	}
    579  1.15    soren 	return retval;
    580  1.15    soren }
    581  1.15    soren 
    582  1.15    soren /*
    583   1.1     kenh  * DEVICE COMMANDS
    584   1.1     kenh  */
    585   1.1     kenh 
    586   1.1     kenh /*
    587   1.1     kenh  * device_identify:
    588   1.1     kenh  *
    589   1.1     kenh  *	Display the identity of the device
    590   1.1     kenh  */
    591   1.1     kenh void
    592  1.13   simonb device_identify(int argc, char *argv[])
    593   1.1     kenh {
    594   1.1     kenh 	struct ataparams *inqbuf;
    595   1.1     kenh 	struct atareq req;
    596   1.1     kenh 	unsigned char inbuf[DEV_BSIZE];
    597   1.2     kenh #if BYTE_ORDER == LITTLE_ENDIAN
    598   1.1     kenh 	int i;
    599   1.1     kenh 	u_int16_t *p;
    600   1.1     kenh #endif
    601   1.1     kenh 
    602   1.1     kenh 	/* No arguments. */
    603   1.1     kenh 	if (argc != 0)
    604   1.5    soren 		usage();
    605   1.1     kenh 
    606   1.1     kenh 	memset(&inbuf, 0, sizeof(inbuf));
    607   1.1     kenh 	memset(&req, 0, sizeof(req));
    608   1.1     kenh 
    609   1.1     kenh 	inqbuf = (struct ataparams *) inbuf;
    610   1.1     kenh 
    611   1.1     kenh 	req.flags = ATACMD_READ;
    612   1.1     kenh 	req.command = WDCC_IDENTIFY;
    613   1.1     kenh 	req.databuf = (caddr_t) inbuf;
    614   1.1     kenh 	req.datalen = sizeof(inbuf);
    615   1.1     kenh 	req.timeout = 1000;
    616   1.1     kenh 
    617   1.1     kenh 	ata_command(&req);
    618   1.1     kenh 
    619   1.1     kenh #if BYTE_ORDER == LITTLE_ENDIAN
    620   1.1     kenh 	/*
    621   1.1     kenh 	 * On little endian machines, we need to shuffle the string
    622   1.1     kenh 	 * byte order.  However, we don't have to do this for NEC or
    623   1.1     kenh 	 * Mitsumi ATAPI devices
    624   1.1     kenh 	 */
    625   1.1     kenh 
    626   1.1     kenh 	if (!((inqbuf->atap_config & WDC_CFG_ATAPI_MASK) == WDC_CFG_ATAPI &&
    627   1.1     kenh 	      ((inqbuf->atap_model[0] == 'N' &&
    628   1.1     kenh 		  inqbuf->atap_model[1] == 'E') ||
    629   1.1     kenh 	       (inqbuf->atap_model[0] == 'F' &&
    630   1.1     kenh 		  inqbuf->atap_model[1] == 'X')))) {
    631   1.1     kenh 		for (i = 0 ; i < sizeof(inqbuf->atap_model); i += 2) {
    632   1.1     kenh 			p = (u_short *) (inqbuf->atap_model + i);
    633   1.1     kenh 			*p = ntohs(*p);
    634   1.1     kenh 		}
    635   1.1     kenh 		for (i = 0 ; i < sizeof(inqbuf->atap_serial); i += 2) {
    636   1.1     kenh 			p = (u_short *) (inqbuf->atap_serial + i);
    637   1.1     kenh 			*p = ntohs(*p);
    638   1.1     kenh 		}
    639   1.1     kenh 		for (i = 0 ; i < sizeof(inqbuf->atap_revision); i += 2) {
    640   1.1     kenh 			p = (u_short *) (inqbuf->atap_revision + i);
    641   1.1     kenh 			*p = ntohs(*p);
    642   1.1     kenh 		}
    643   1.1     kenh 	}
    644   1.1     kenh #endif
    645   1.1     kenh 
    646   1.1     kenh 	/*
    647   1.1     kenh 	 * Strip blanks off of the info strings.  Yuck, I wish this was
    648   1.1     kenh 	 * cleaner.
    649   1.1     kenh 	 */
    650   1.1     kenh 
    651   1.1     kenh 	if (inqbuf->atap_model[sizeof(inqbuf->atap_model) - 1] == ' ') {
    652   1.1     kenh 		inqbuf->atap_model[sizeof(inqbuf->atap_model) - 1] = '\0';
    653   1.1     kenh 		while (inqbuf->atap_model[strlen(inqbuf->atap_model) - 1] == ' ')
    654   1.1     kenh 			inqbuf->atap_model[strlen(inqbuf->atap_model) - 1] = '\0';
    655   1.1     kenh 	}
    656   1.1     kenh 
    657   1.1     kenh 	if (inqbuf->atap_revision[sizeof(inqbuf->atap_revision) - 1] == ' ') {
    658   1.1     kenh 		inqbuf->atap_revision[sizeof(inqbuf->atap_revision) - 1] = '\0';
    659   1.1     kenh 		while (inqbuf->atap_revision[strlen(inqbuf->atap_revision) - 1] == ' ')
    660   1.1     kenh 			inqbuf->atap_revision[strlen(inqbuf->atap_revision) - 1] = '\0';
    661   1.1     kenh 	}
    662   1.1     kenh 
    663   1.1     kenh 	if (inqbuf->atap_serial[sizeof(inqbuf->atap_serial) - 1] == ' ') {
    664   1.1     kenh 		inqbuf->atap_serial[sizeof(inqbuf->atap_serial) - 1] = '\0';
    665   1.1     kenh 		while (inqbuf->atap_serial[strlen(inqbuf->atap_serial) - 1] == ' ')
    666   1.1     kenh 			inqbuf->atap_serial[strlen(inqbuf->atap_serial) - 1] = '\0';
    667   1.1     kenh 	}
    668   1.1     kenh 
    669   1.1     kenh 	printf("Model: %.*s, Rev: %.*s, Serial #: %.*s\n",
    670   1.1     kenh 	       (int) sizeof(inqbuf->atap_model), inqbuf->atap_model,
    671   1.1     kenh 	       (int) sizeof(inqbuf->atap_revision), inqbuf->atap_revision,
    672   1.1     kenh 	       (int) sizeof(inqbuf->atap_serial), inqbuf->atap_serial);
    673   1.1     kenh 
    674   1.1     kenh 	printf("Device type: %s, %s\n", inqbuf->atap_config & WDC_CFG_ATAPI ?
    675   1.1     kenh 	       "ATAPI" : "ATA", inqbuf->atap_config & ATA_CFG_FIXED ? "fixed" :
    676   1.1     kenh 	       "removable");
    677   1.1     kenh 
    678   1.1     kenh 	if ((inqbuf->atap_config & WDC_CFG_ATAPI_MASK) == 0)
    679   1.1     kenh 		printf("Cylinders: %d, heads: %d, sec/track: %d, total "
    680   1.1     kenh 		       "sectors: %d\n", inqbuf->atap_cylinders,
    681   1.1     kenh 		       inqbuf->atap_heads, inqbuf->atap_sectors,
    682   1.1     kenh 		       (inqbuf->atap_capacity[1] << 16) |
    683   1.1     kenh 		       inqbuf->atap_capacity[0]);
    684   1.1     kenh 
    685   1.1     kenh 	if (inqbuf->atap_queuedepth & WDC_QUEUE_DEPTH_MASK)
    686   1.1     kenh 		printf("Device supports command queue depth of %d\n",
    687   1.1     kenh 		       inqbuf->atap_queuedepth & 0xf);
    688   1.1     kenh 
    689   1.1     kenh 	printf("Device capabilities:\n");
    690  1.10       is 	print_bitinfo("\t", "\n", inqbuf->atap_capabilities1, ata_caps);
    691   1.1     kenh 
    692   1.1     kenh 	if (inqbuf->atap_ata_major != 0 && inqbuf->atap_ata_major != 0xffff) {
    693   1.1     kenh 		printf("Device supports following standards:\n");
    694  1.10       is 		print_bitinfo("", " ", inqbuf->atap_ata_major, ata_vers);
    695   1.1     kenh 		printf("\n");
    696   1.1     kenh 	}
    697   1.1     kenh 
    698   1.1     kenh 	if (inqbuf->atap_cmd_set1 != 0 && inqbuf->atap_cmd_set1 != 0xffff &&
    699   1.1     kenh 	    inqbuf->atap_cmd_set2 != 0 && inqbuf->atap_cmd_set2 != 0xffff) {
    700   1.1     kenh 		printf("Command set support:\n");
    701  1.10       is 		print_bitinfo("\t", "\n", inqbuf->atap_cmd_set1, ata_cmd_set1);
    702  1.10       is 		print_bitinfo("\t", "\n", inqbuf->atap_cmd_set2, ata_cmd_set2);
    703  1.23     yamt 		if (inqbuf->atap_cmd_ext != 0 && inqbuf->atap_cmd_ext != 0xffff)
    704  1.23     yamt 			print_bitinfo("\t", "\n", inqbuf->atap_cmd_ext,
    705  1.23     yamt 			    ata_cmd_ext);
    706   1.1     kenh 	}
    707   1.1     kenh 
    708   1.1     kenh 	if (inqbuf->atap_cmd_def != 0 && inqbuf->atap_cmd_def != 0xffff) {
    709   1.1     kenh 		printf("Command sets/features enabled:\n");
    710  1.14   simonb 		print_bitinfo("\t", "\n", inqbuf->atap_cmd1_en &
    711   1.1     kenh 			      (WDC_CMD1_SRV | WDC_CMD1_RLSE | WDC_CMD1_AHEAD |
    712   1.1     kenh 			       WDC_CMD1_CACHE | WDC_CMD1_SEC | WDC_CMD1_SMART),
    713   1.1     kenh 			       ata_cmd_set1);
    714  1.14   simonb 		print_bitinfo("\t", "\n", inqbuf->atap_cmd2_en &
    715   1.1     kenh 			      (WDC_CMD2_RMSN | ATA_CMD2_APM), ata_cmd_set2);
    716   1.1     kenh 	}
    717   1.1     kenh 
    718   1.1     kenh 	return;
    719   1.1     kenh }
    720   1.1     kenh 
    721   1.1     kenh /*
    722   1.1     kenh  * device idle:
    723   1.1     kenh  *
    724   1.1     kenh  * issue the IDLE IMMEDIATE command to the drive
    725   1.1     kenh  */
    726   1.1     kenh 
    727   1.1     kenh void
    728  1.13   simonb device_idle(int argc, char *argv[])
    729   1.1     kenh {
    730   1.1     kenh 	struct atareq req;
    731   1.1     kenh 
    732   1.1     kenh 	/* No arguments. */
    733   1.1     kenh 	if (argc != 0)
    734   1.5    soren 		usage();
    735   1.1     kenh 
    736   1.1     kenh 	memset(&req, 0, sizeof(req));
    737   1.1     kenh 
    738   1.1     kenh 	if (strcmp(cmdname, "idle") == 0)
    739   1.1     kenh 		req.command = WDCC_IDLE_IMMED;
    740   1.1     kenh 	else if (strcmp(cmdname, "standby") == 0)
    741   1.1     kenh 		req.command = WDCC_STANDBY_IMMED;
    742   1.1     kenh 	else
    743   1.1     kenh 		req.command = WDCC_SLEEP;
    744   1.1     kenh 
    745   1.1     kenh 	req.timeout = 1000;
    746   1.1     kenh 
    747   1.1     kenh 	ata_command(&req);
    748   1.1     kenh 
    749   1.1     kenh 	return;
    750   1.1     kenh }
    751   1.1     kenh 
    752   1.1     kenh /*
    753   1.1     kenh  * Set the idle timer on the disk.  Set it for either idle mode or
    754   1.1     kenh  * standby mode, depending on how we were invoked.
    755   1.1     kenh  */
    756   1.1     kenh 
    757   1.1     kenh void
    758  1.13   simonb device_setidle(int argc, char *argv[])
    759   1.1     kenh {
    760   1.1     kenh 	unsigned long idle;
    761   1.1     kenh 	struct atareq req;
    762   1.1     kenh 	char *end;
    763   1.1     kenh 
    764   1.1     kenh 	/* Only one argument */
    765   1.1     kenh 	if (argc != 1)
    766   1.5    soren 		usage();
    767   1.1     kenh 
    768   1.1     kenh 	idle = strtoul(argv[0], &end, 0);
    769   1.1     kenh 
    770   1.1     kenh 	if (*end != '\0') {
    771   1.1     kenh 		fprintf(stderr, "Invalid idle time: \"%s\"\n", argv[0]);
    772   1.1     kenh 		exit(1);
    773   1.1     kenh 	}
    774   1.1     kenh 
    775   1.1     kenh 	if (idle > 19800) {
    776   1.1     kenh 		fprintf(stderr, "Idle time has a maximum value of 5.5 "
    777   1.1     kenh 			"hours\n");
    778   1.1     kenh 		exit(1);
    779   1.1     kenh 	}
    780   1.1     kenh 
    781   1.1     kenh 	if (idle != 0 && idle < 5) {
    782   1.1     kenh 		fprintf(stderr, "Idle timer must be at least 5 seconds\n");
    783   1.1     kenh 		exit(1);
    784   1.1     kenh 	}
    785   1.1     kenh 
    786   1.1     kenh 	memset(&req, 0, sizeof(req));
    787   1.1     kenh 
    788   1.1     kenh 	if (idle <= 240*5)
    789   1.1     kenh 		req.sec_count = idle / 5;
    790   1.1     kenh 	else
    791   1.1     kenh 		req.sec_count = idle / (30*60) + 240;
    792   1.1     kenh 
    793   1.1     kenh 	req.command = cmdname[3] == 's' ? WDCC_STANDBY : WDCC_IDLE;
    794   1.1     kenh 	req.timeout = 1000;
    795   1.1     kenh 
    796   1.1     kenh 	ata_command(&req);
    797   1.1     kenh 
    798   1.1     kenh 	return;
    799   1.3     kenh }
    800   1.3     kenh 
    801   1.3     kenh /*
    802   1.3     kenh  * Query the device for the current power mode
    803   1.3     kenh  */
    804   1.3     kenh 
    805   1.3     kenh void
    806  1.13   simonb device_checkpower(int argc, char *argv[])
    807   1.3     kenh {
    808   1.3     kenh 	struct atareq req;
    809   1.3     kenh 
    810   1.3     kenh 	/* No arguments. */
    811   1.3     kenh 	if (argc != 0)
    812   1.5    soren 		usage();
    813   1.3     kenh 
    814   1.3     kenh 	memset(&req, 0, sizeof(req));
    815   1.3     kenh 
    816   1.3     kenh 	req.command = WDCC_CHECK_PWR;
    817   1.3     kenh 	req.timeout = 1000;
    818   1.3     kenh 	req.flags = ATACMD_READREG;
    819   1.3     kenh 
    820   1.3     kenh 	ata_command(&req);
    821   1.3     kenh 
    822   1.3     kenh 	printf("Current power status: ");
    823   1.3     kenh 
    824   1.3     kenh 	switch (req.sec_count) {
    825   1.3     kenh 	case 0x00:
    826   1.3     kenh 		printf("Standby mode\n");
    827   1.3     kenh 		break;
    828   1.3     kenh 	case 0x80:
    829   1.3     kenh 		printf("Idle mode\n");
    830   1.3     kenh 		break;
    831   1.3     kenh 	case 0xff:
    832   1.3     kenh 		printf("Active mode\n");
    833   1.3     kenh 		break;
    834   1.3     kenh 	default:
    835   1.3     kenh 		printf("Unknown power code (%02x)\n", req.sec_count);
    836   1.3     kenh 	}
    837   1.3     kenh 
    838  1.15    soren 	return;
    839  1.15    soren }
    840  1.15    soren 
    841  1.15    soren /*
    842  1.15    soren  * device_smart:
    843  1.15    soren  *
    844  1.15    soren  *	Display SMART status
    845  1.15    soren  */
    846  1.15    soren void
    847  1.15    soren device_smart(int argc, char *argv[])
    848  1.15    soren {
    849  1.15    soren 	struct atareq req;
    850  1.15    soren 	unsigned char inbuf[DEV_BSIZE];
    851  1.15    soren 	unsigned char inbuf2[DEV_BSIZE];
    852  1.15    soren 
    853  1.15    soren 	/* Only one argument */
    854  1.15    soren 	if (argc != 1)
    855  1.15    soren 		usage();
    856  1.15    soren 
    857  1.15    soren 	if (strcmp(argv[0], "enable") == 0) {
    858  1.20  mycroft 		memset(&req, 0, sizeof(req));
    859  1.15    soren 
    860  1.20  mycroft 		req.features = WDSM_ENABLE_OPS;
    861  1.20  mycroft 		req.command = WDCC_SMART;
    862  1.20  mycroft 		req.cylinder = htole16(WDSMART_CYL);
    863  1.20  mycroft 		req.timeout = 1000;
    864  1.15    soren 
    865  1.20  mycroft 		ata_command(&req);
    866  1.15    soren 
    867  1.20  mycroft 		is_smart();
    868  1.15    soren 	} else if (strcmp(argv[0], "disable") == 0) {
    869  1.20  mycroft 		memset(&req, 0, sizeof(req));
    870  1.15    soren 
    871  1.20  mycroft 		req.features = WDSM_DISABLE_OPS;
    872  1.20  mycroft 		req.command = WDCC_SMART;
    873  1.20  mycroft 		req.cylinder = htole16(WDSMART_CYL);
    874  1.20  mycroft 		req.timeout = 1000;
    875  1.15    soren 
    876  1.20  mycroft 		ata_command(&req);
    877  1.15    soren 
    878  1.20  mycroft 		is_smart();
    879  1.16    soren 	} else if (strcmp(argv[0], "status") == 0) {
    880  1.24      lha 		if (!is_smart()) {
    881  1.24      lha 			fprintf(stderr, "SMART not supported\n");
    882  1.24      lha 			return;
    883  1.24      lha 		}
    884  1.24      lha 
    885  1.15    soren 			memset(&inbuf, 0, sizeof(inbuf));
    886  1.15    soren 			memset(&req, 0, sizeof(req));
    887  1.15    soren 
    888  1.15    soren 			req.features = WDSM_STATUS;
    889  1.15    soren 			req.command = WDCC_SMART;
    890  1.15    soren 			req.cylinder = htole16(WDSMART_CYL);
    891  1.15    soren 			req.timeout = 1000;
    892  1.15    soren 
    893  1.15    soren 			ata_command(&req);
    894  1.15    soren 
    895  1.15    soren 			if (req.cylinder != htole16(WDSMART_CYL)) {
    896  1.15    soren 				fprintf(stderr, "Threshold exceeds condition\n");
    897  1.15    soren 			}
    898  1.15    soren 
    899  1.15    soren 			/* WDSM_RD_DATA and WDSM_RD_THRESHOLDS are optional
    900  1.15    soren 			 * features, the following ata_command()'s may error
    901  1.15    soren 			 * and exit().
    902  1.15    soren 			 */
    903  1.15    soren 
    904  1.15    soren 			memset(&inbuf, 0, sizeof(inbuf));
    905  1.15    soren 			memset(&req, 0, sizeof(req));
    906  1.15    soren 
    907  1.15    soren 			req.flags = ATACMD_READ;
    908  1.15    soren 			req.features = WDSM_RD_DATA;
    909  1.15    soren 			req.command = WDCC_SMART;
    910  1.15    soren 			req.databuf = (caddr_t) inbuf;
    911  1.15    soren 			req.datalen = sizeof(inbuf);
    912  1.15    soren 			req.cylinder = htole16(WDSMART_CYL);
    913  1.15    soren 			req.timeout = 1000;
    914  1.15    soren 
    915  1.15    soren 			ata_command(&req);
    916  1.15    soren 
    917  1.15    soren 			memset(&inbuf2, 0, sizeof(inbuf2));
    918  1.15    soren 			memset(&req, 0, sizeof(req));
    919  1.15    soren 
    920  1.15    soren 			req.flags = ATACMD_READ;
    921  1.15    soren 			req.features = WDSM_RD_THRESHOLDS;
    922  1.15    soren 			req.command = WDCC_SMART;
    923  1.15    soren 			req.databuf = (caddr_t) inbuf2;
    924  1.15    soren 			req.datalen = sizeof(inbuf2);
    925  1.15    soren 			req.cylinder = htole16(WDSMART_CYL);
    926  1.15    soren 			req.timeout = 1000;
    927  1.15    soren 
    928  1.15    soren 			ata_command(&req);
    929  1.15    soren 
    930  1.15    soren 			print_smart_status(inbuf, inbuf2);
    931  1.24      lha 
    932  1.24      lha 	} else if (strcmp(argv[0], "selftest-log") == 0) {
    933  1.24      lha 		if (!is_smart()) {
    934  1.15    soren 			fprintf(stderr, "SMART not supported\n");
    935  1.24      lha 			return;
    936  1.15    soren 		}
    937  1.24      lha 
    938  1.24      lha 		memset(&inbuf, 0, sizeof(inbuf));
    939  1.24      lha 		memset(&req, 0, sizeof(req));
    940  1.24      lha 
    941  1.24      lha 		req.flags = ATACMD_READ;
    942  1.24      lha 		req.features = WDSM_RD_LOG;
    943  1.24      lha 		req.sec_count = 1;
    944  1.24      lha 		req.sec_num = 6;
    945  1.24      lha 		req.command = WDCC_SMART;
    946  1.24      lha 		req.databuf = (caddr_t) inbuf;
    947  1.24      lha 		req.datalen = sizeof(inbuf);
    948  1.24      lha 		req.cylinder = htole16(WDSMART_CYL);
    949  1.24      lha 		req.timeout = 1000;
    950  1.24      lha 
    951  1.24      lha 		ata_command(&req);
    952  1.24      lha 
    953  1.24      lha 		print_selftest(inbuf);
    954  1.24      lha 
    955  1.15    soren 	} else {
    956  1.15    soren 		usage();
    957  1.15    soren 	}
    958   1.3     kenh 	return;
    959   1.1     kenh }
    960