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