Home | History | Annotate | Line # | Download | only in atactl
atactl.c revision 1.29
      1 /*	$NetBSD: atactl.c,v 1.29 2004/03/28 01:23:15 mycroft 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.29 2004/03/28 01:23:15 mycroft 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 *, uint64_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 *, uint64_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, uint64_t raw_value)
    345 {
    346 	printf("%" PRIu8, attr->raw[0]);
    347 	if (attr->raw[0] != raw_value)
    348 		printf(" Lifetime max/min %" PRIu8 "/%" PRIu8,
    349 		    attr->raw[2], attr->raw[4]);
    350 }
    351 
    352 
    353 /*
    354  * Print out SMART attribute thresholds and values
    355  */
    356 
    357 void
    358 print_smart_status(void *vbuf, void *tbuf)
    359 {
    360 	struct ata_smart_attributes *value_buf = vbuf;
    361 	struct ata_smart_thresholds *threshold_buf = tbuf;
    362 	struct ata_smart_attr *attr;
    363 	uint64_t raw_value;
    364 	int flags;
    365 	int i, j;
    366 	int aid;
    367 	int8_t checksum;
    368 
    369 	for (i = checksum = 0; i < 511; i++)
    370 		checksum += ((int8_t *) value_buf)[i];
    371 	checksum *= -1;
    372 	if (checksum != value_buf->checksum) {
    373 		fprintf(stderr, "SMART attribute values checksum error\n");
    374 		return;
    375 	}
    376 
    377 	for (i = checksum = 0; i < 511; i++)
    378 		checksum += ((int8_t *) threshold_buf)[i];
    379 	checksum *= -1;
    380 	if (checksum != threshold_buf->checksum) {
    381 		fprintf(stderr, "SMART attribute thresholds checksum error\n");
    382 		return;
    383 	}
    384 
    385 	printf("id value thresh crit collect reliability description\t\t\traw\n");
    386 	for (i = 0; i < 256; i++) {
    387 		int thresh = 0;
    388 
    389 		attr = NULL;
    390 
    391 		for (j = 0; j < 30; j++) {
    392 			if (value_buf->attributes[j].id == i)
    393 				attr = &value_buf->attributes[j];
    394 			if (threshold_buf->thresholds[j].id == i)
    395 				thresh = threshold_buf->thresholds[j].value;
    396 	}
    397 
    398 		if (thresh && attr == NULL)
    399 			errx(1, "threshold but not attr %d", i);
    400 		if (attr == NULL)
    401 			continue;
    402 
    403 		if (attr->value == 0||attr->value == 0xFE||attr->value == 0xFF)
    404 			continue;
    405 
    406 		for (aid = 0;
    407 		     smart_attrs[aid].id != i && smart_attrs[aid].id != 0;
    408 		     aid++)
    409 			;
    410 
    411 		flags = attr->flags;
    412 
    413 		printf("%3d %3d  %3d     %-3s %-7s %stive    %-24s\t",
    414 		    i, attr->value, thresh,
    415 		    flags & WDSM_ATTR_ADVISORY ? "yes" : "no",
    416 		    flags & WDSM_ATTR_COLLECTIVE ? "online" : "offline",
    417 		    attr->value > thresh ? "posi" : "nega",
    418 		    smart_attrs[aid].name);
    419 
    420 		for (j = 0, raw_value = 0; j < 6; j++)
    421 			raw_value += ((uint64_t)attr->raw[j]) << (8*j);
    422 
    423 		if (smart_attrs[aid].special)
    424 			(*smart_attrs[aid].special)(attr, raw_value);
    425 		else
    426 			printf("%" PRIu64, raw_value);
    427 		printf("\n");
    428 		}
    429 	}
    430 
    431 struct {
    432 	int number;
    433 	const char *name;
    434 } selftest_name[] = {
    435 	{ 0, "Off-line" },
    436 	{ 1, "Short off-line" },
    437 	{ 2, "Extended off-line" },
    438 	{ 127, "Abort off-line test" },
    439 	{ 129, "Short captive" },
    440 	{ 130, "Extended captive" },
    441 	{ 256, "Unknown test" }, /* larger then u_int8_t */
    442 	{ 0, NULL }
    443 };
    444 
    445 const char *selftest_status[] = {
    446 	"No error",
    447 	"Aborted by the host",
    448 	"Interruped by the host by reset",
    449 	"Fatal error or unknown test error",
    450 	"Unknown test element failed",
    451 	"Electrical test element failed",
    452 	"The Servo (and/or seek) test element failed",
    453 	"Read element of test failed",
    454 	"Reserved",
    455 	"Reserved",
    456 	"Reserved",
    457 	"Reserved",
    458 	"Reserved",
    459 	"Reserved",
    460 	"Reserved",
    461 	"Self-test in progress"
    462 };
    463 
    464 void
    465 print_selftest_entry(int num, struct ata_smart_selftest *le)
    466 {
    467 	unsigned char *p;
    468 	int i;
    469 
    470 	/* check if all zero */
    471 	for (p = (void *)le, i = 0; i < sizeof(*le); i++)
    472 		if (p[i] != 0)
    473 			break;
    474 	if (i == sizeof(*le))
    475 		return;
    476 
    477 	printf("Log entry: %d\n", num);
    478 
    479 	/* Get test name */
    480 	for (i = 0; selftest_name[i].name != NULL; i++)
    481 		if (selftest_name[i].number == le->number)
    482 			break;
    483 	if (selftest_name[i].number == 0)
    484 		i = 255; /* unknown test */
    485 
    486 	printf("\tName: %s\n", selftest_name[i].name);
    487 	printf("\tStatus: %s\n", selftest_status[le->status >> 4]);
    488 	if (le->status >> 4 == 15)
    489 		printf("\tPrecent of test remaning: %1d0\n", le->status & 0xf);
    490 	if (le->status)
    491 		printf("LBA first error: %d\n", le->lba_first_error);
    492 }
    493 
    494 void
    495 print_selftest(void *buf)
    496 {
    497 	struct ata_smart_selftestlog *stlog = buf;
    498 	int8_t checksum;
    499 	int i;
    500 
    501 	for (i = checksum = 0; i < 511; i++)
    502 		checksum += ((int8_t *) buf)[i];
    503   	checksum *= -1;
    504 	if ((u_int8_t)checksum != stlog->checksum) {
    505 		fprintf(stderr, "SMART selftest log checksum error\n");
    506 		return;
    507 	}
    508 
    509 	if (stlog->data_structure_revision != 1) {
    510 		fprintf(stderr, "Log revision not 1");
    511 		return;
    512 	}
    513 
    514 	if (stlog->mostrecenttest == 0) {
    515 		printf("No self-tests have been logged\n");
    516 		return;
    517 	}
    518 
    519 	if (stlog->mostrecenttest > 22) {
    520 		fprintf(stderr, "Most recent test is too large\n");
    521 		return;
    522 	}
    523 
    524 	for (i = stlog->mostrecenttest; i < 22; i++)
    525 		print_selftest_entry(i, &stlog->log_entries[i]);
    526 	for (i = 0; i < stlog->mostrecenttest; i++)
    527 		print_selftest_entry(i, &stlog->log_entries[i]);
    528 }
    529 
    530 /*
    531  * is_smart:
    532  *
    533  *	Detect whether device supports SMART and SMART is enabled.
    534  */
    535 
    536 int
    537 is_smart(void)
    538 {
    539 	int retval = 0;
    540 	struct atareq req;
    541 	unsigned char inbuf[DEV_BSIZE];
    542 	struct ataparams *inqbuf;
    543 	char *status;
    544 
    545 	memset(&inbuf, 0, sizeof(inbuf));
    546 	memset(&req, 0, sizeof(req));
    547 
    548 	inqbuf = (struct ataparams *) inbuf;
    549 
    550 	req.flags = ATACMD_READ;
    551 	req.command = WDCC_IDENTIFY;
    552 	req.databuf = (caddr_t) inbuf;
    553 	req.datalen = sizeof(inbuf);
    554 	req.timeout = 1000;
    555 
    556 	ata_command(&req);
    557 
    558 	if (inqbuf->atap_cmd_def != 0 && inqbuf->atap_cmd_def != 0xffff) {
    559 		if (!(inqbuf->atap_cmd_set1 & WDC_CMD1_SMART)) {
    560 			fprintf(stderr, "SMART unsupported\n");
    561 		} else {
    562 			if (inqbuf->atap_ata_major <= WDC_VER_ATA5 ||
    563 			    inqbuf->atap_cmd_set2 == 0xffff ||
    564 			    inqbuf->atap_cmd_set2 == 0x0000) {
    565 				status = "status unknown";
    566 				retval = 2;
    567 			} else {
    568 				if (inqbuf->atap_cmd1_en & WDC_CMD1_SMART) {
    569 					status = "enabled";
    570 					retval = 1;
    571 				} else {
    572 					status = "disabled";
    573 				}
    574 			}
    575 			printf("SMART supported, SMART %s\n", status);
    576 		}
    577 	}
    578 	return retval;
    579 }
    580 
    581 /*
    582  * DEVICE COMMANDS
    583  */
    584 
    585 /*
    586  * device_identify:
    587  *
    588  *	Display the identity of the device
    589  */
    590 void
    591 device_identify(int argc, char *argv[])
    592 {
    593 	struct ataparams *inqbuf;
    594 	struct atareq req;
    595 	unsigned char inbuf[DEV_BSIZE];
    596 #if BYTE_ORDER == LITTLE_ENDIAN
    597 	int i;
    598 	u_int16_t *p;
    599 #endif
    600 
    601 	/* No arguments. */
    602 	if (argc != 0)
    603 		usage();
    604 
    605 	memset(&inbuf, 0, sizeof(inbuf));
    606 	memset(&req, 0, sizeof(req));
    607 
    608 	inqbuf = (struct ataparams *) inbuf;
    609 
    610 	req.flags = ATACMD_READ;
    611 	req.command = WDCC_IDENTIFY;
    612 	req.databuf = (caddr_t) inbuf;
    613 	req.datalen = sizeof(inbuf);
    614 	req.timeout = 1000;
    615 
    616 	ata_command(&req);
    617 
    618 #if BYTE_ORDER == LITTLE_ENDIAN
    619 	/*
    620 	 * On little endian machines, we need to shuffle the string
    621 	 * byte order.  However, we don't have to do this for NEC or
    622 	 * Mitsumi ATAPI devices
    623 	 */
    624 
    625 	if (!((inqbuf->atap_config & WDC_CFG_ATAPI_MASK) == WDC_CFG_ATAPI &&
    626 	      ((inqbuf->atap_model[0] == 'N' &&
    627 		  inqbuf->atap_model[1] == 'E') ||
    628 	       (inqbuf->atap_model[0] == 'F' &&
    629 		  inqbuf->atap_model[1] == 'X')))) {
    630 		for (i = 0 ; i < sizeof(inqbuf->atap_model); i += 2) {
    631 			p = (u_short *) (inqbuf->atap_model + i);
    632 			*p = ntohs(*p);
    633 		}
    634 		for (i = 0 ; i < sizeof(inqbuf->atap_serial); i += 2) {
    635 			p = (u_short *) (inqbuf->atap_serial + i);
    636 			*p = ntohs(*p);
    637 		}
    638 		for (i = 0 ; i < sizeof(inqbuf->atap_revision); i += 2) {
    639 			p = (u_short *) (inqbuf->atap_revision + i);
    640 			*p = ntohs(*p);
    641 		}
    642 	}
    643 #endif
    644 
    645 	/*
    646 	 * Strip blanks off of the info strings.  Yuck, I wish this was
    647 	 * cleaner.
    648 	 */
    649 
    650 	if (inqbuf->atap_model[sizeof(inqbuf->atap_model) - 1] == ' ') {
    651 		inqbuf->atap_model[sizeof(inqbuf->atap_model) - 1] = '\0';
    652 		while (inqbuf->atap_model[strlen(inqbuf->atap_model) - 1] == ' ')
    653 			inqbuf->atap_model[strlen(inqbuf->atap_model) - 1] = '\0';
    654 	}
    655 
    656 	if (inqbuf->atap_revision[sizeof(inqbuf->atap_revision) - 1] == ' ') {
    657 		inqbuf->atap_revision[sizeof(inqbuf->atap_revision) - 1] = '\0';
    658 		while (inqbuf->atap_revision[strlen(inqbuf->atap_revision) - 1] == ' ')
    659 			inqbuf->atap_revision[strlen(inqbuf->atap_revision) - 1] = '\0';
    660 	}
    661 
    662 	if (inqbuf->atap_serial[sizeof(inqbuf->atap_serial) - 1] == ' ') {
    663 		inqbuf->atap_serial[sizeof(inqbuf->atap_serial) - 1] = '\0';
    664 		while (inqbuf->atap_serial[strlen(inqbuf->atap_serial) - 1] == ' ')
    665 			inqbuf->atap_serial[strlen(inqbuf->atap_serial) - 1] = '\0';
    666 	}
    667 
    668 	printf("Model: %.*s, Rev: %.*s, Serial #: %.*s\n",
    669 	       (int) sizeof(inqbuf->atap_model), inqbuf->atap_model,
    670 	       (int) sizeof(inqbuf->atap_revision), inqbuf->atap_revision,
    671 	       (int) sizeof(inqbuf->atap_serial), inqbuf->atap_serial);
    672 
    673 	printf("Device type: %s, %s\n", inqbuf->atap_config & WDC_CFG_ATAPI ?
    674 	       "ATAPI" : "ATA", inqbuf->atap_config & ATA_CFG_FIXED ? "fixed" :
    675 	       "removable");
    676 
    677 	if ((inqbuf->atap_config & WDC_CFG_ATAPI_MASK) == 0)
    678 		printf("Cylinders: %d, heads: %d, sec/track: %d, total "
    679 		       "sectors: %d\n", inqbuf->atap_cylinders,
    680 		       inqbuf->atap_heads, inqbuf->atap_sectors,
    681 		       (inqbuf->atap_capacity[1] << 16) |
    682 		       inqbuf->atap_capacity[0]);
    683 
    684 	if (inqbuf->atap_queuedepth & WDC_QUEUE_DEPTH_MASK)
    685 		printf("Device supports command queue depth of %d\n",
    686 		       inqbuf->atap_queuedepth & 0xf);
    687 
    688 	printf("Device capabilities:\n");
    689 	print_bitinfo("\t", "\n", inqbuf->atap_capabilities1, ata_caps);
    690 
    691 	if (inqbuf->atap_ata_major != 0 && inqbuf->atap_ata_major != 0xffff) {
    692 		printf("Device supports following standards:\n");
    693 		print_bitinfo("", " ", inqbuf->atap_ata_major, ata_vers);
    694 		printf("\n");
    695 	}
    696 
    697 	if (inqbuf->atap_cmd_set1 != 0 && inqbuf->atap_cmd_set1 != 0xffff &&
    698 	    inqbuf->atap_cmd_set2 != 0 && inqbuf->atap_cmd_set2 != 0xffff) {
    699 		printf("Command set support:\n");
    700 		print_bitinfo("\t", "\n", inqbuf->atap_cmd_set1, ata_cmd_set1);
    701 		print_bitinfo("\t", "\n", inqbuf->atap_cmd_set2, ata_cmd_set2);
    702 		if (inqbuf->atap_cmd_ext != 0 && inqbuf->atap_cmd_ext != 0xffff)
    703 			print_bitinfo("\t", "\n", inqbuf->atap_cmd_ext,
    704 			    ata_cmd_ext);
    705 	}
    706 
    707 	if (inqbuf->atap_cmd_def != 0 && inqbuf->atap_cmd_def != 0xffff) {
    708 		printf("Command sets/features enabled:\n");
    709 		print_bitinfo("\t", "\n", inqbuf->atap_cmd1_en &
    710 			      (WDC_CMD1_SRV | WDC_CMD1_RLSE | WDC_CMD1_AHEAD |
    711 			       WDC_CMD1_CACHE | WDC_CMD1_SEC | WDC_CMD1_SMART),
    712 			       ata_cmd_set1);
    713 		print_bitinfo("\t", "\n", inqbuf->atap_cmd2_en &
    714 			      (WDC_CMD2_RMSN | ATA_CMD2_APM), ata_cmd_set2);
    715 	}
    716 
    717 	return;
    718 }
    719 
    720 /*
    721  * device idle:
    722  *
    723  * issue the IDLE IMMEDIATE command to the drive
    724  */
    725 
    726 void
    727 device_idle(int argc, char *argv[])
    728 {
    729 	struct atareq req;
    730 
    731 	/* No arguments. */
    732 	if (argc != 0)
    733 		usage();
    734 
    735 	memset(&req, 0, sizeof(req));
    736 
    737 	if (strcmp(cmdname, "idle") == 0)
    738 		req.command = WDCC_IDLE_IMMED;
    739 	else if (strcmp(cmdname, "standby") == 0)
    740 		req.command = WDCC_STANDBY_IMMED;
    741 	else
    742 		req.command = WDCC_SLEEP;
    743 
    744 	req.timeout = 1000;
    745 
    746 	ata_command(&req);
    747 
    748 	return;
    749 }
    750 
    751 /*
    752  * Set the idle timer on the disk.  Set it for either idle mode or
    753  * standby mode, depending on how we were invoked.
    754  */
    755 
    756 void
    757 device_setidle(int argc, char *argv[])
    758 {
    759 	unsigned long idle;
    760 	struct atareq req;
    761 	char *end;
    762 
    763 	/* Only one argument */
    764 	if (argc != 1)
    765 		usage();
    766 
    767 	idle = strtoul(argv[0], &end, 0);
    768 
    769 	if (*end != '\0') {
    770 		fprintf(stderr, "Invalid idle time: \"%s\"\n", argv[0]);
    771 		exit(1);
    772 	}
    773 
    774 	if (idle > 19800) {
    775 		fprintf(stderr, "Idle time has a maximum value of 5.5 "
    776 			"hours\n");
    777 		exit(1);
    778 	}
    779 
    780 	if (idle != 0 && idle < 5) {
    781 		fprintf(stderr, "Idle timer must be at least 5 seconds\n");
    782 		exit(1);
    783 	}
    784 
    785 	memset(&req, 0, sizeof(req));
    786 
    787 	if (idle <= 240*5)
    788 		req.sec_count = idle / 5;
    789 	else
    790 		req.sec_count = idle / (30*60) + 240;
    791 
    792 	req.command = cmdname[3] == 's' ? WDCC_STANDBY : WDCC_IDLE;
    793 	req.timeout = 1000;
    794 
    795 	ata_command(&req);
    796 
    797 	return;
    798 }
    799 
    800 /*
    801  * Query the device for the current power mode
    802  */
    803 
    804 void
    805 device_checkpower(int argc, char *argv[])
    806 {
    807 	struct atareq req;
    808 
    809 	/* No arguments. */
    810 	if (argc != 0)
    811 		usage();
    812 
    813 	memset(&req, 0, sizeof(req));
    814 
    815 	req.command = WDCC_CHECK_PWR;
    816 	req.timeout = 1000;
    817 	req.flags = ATACMD_READREG;
    818 
    819 	ata_command(&req);
    820 
    821 	printf("Current power status: ");
    822 
    823 	switch (req.sec_count) {
    824 	case 0x00:
    825 		printf("Standby mode\n");
    826 		break;
    827 	case 0x80:
    828 		printf("Idle mode\n");
    829 		break;
    830 	case 0xff:
    831 		printf("Active mode\n");
    832 		break;
    833 	default:
    834 		printf("Unknown power code (%02x)\n", req.sec_count);
    835 	}
    836 
    837 	return;
    838 }
    839 
    840 /*
    841  * device_smart:
    842  *
    843  *	Display SMART status
    844  */
    845 void
    846 device_smart(int argc, char *argv[])
    847 {
    848 	struct atareq req;
    849 	unsigned char inbuf[DEV_BSIZE];
    850 	unsigned char inbuf2[DEV_BSIZE];
    851 
    852 	/* Only one argument */
    853 	if (argc != 1)
    854 		usage();
    855 
    856 	if (strcmp(argv[0], "enable") == 0) {
    857 		memset(&req, 0, sizeof(req));
    858 
    859 		req.features = WDSM_ENABLE_OPS;
    860 		req.command = WDCC_SMART;
    861 		req.cylinder = htole16(WDSMART_CYL);
    862 		req.timeout = 1000;
    863 
    864 		ata_command(&req);
    865 
    866 		is_smart();
    867 	} else if (strcmp(argv[0], "disable") == 0) {
    868 		memset(&req, 0, sizeof(req));
    869 
    870 		req.features = WDSM_DISABLE_OPS;
    871 		req.command = WDCC_SMART;
    872 		req.cylinder = htole16(WDSMART_CYL);
    873 		req.timeout = 1000;
    874 
    875 		ata_command(&req);
    876 
    877 		is_smart();
    878 	} else if (strcmp(argv[0], "status") == 0) {
    879 		if (!is_smart()) {
    880 			fprintf(stderr, "SMART not supported\n");
    881 			return;
    882 		}
    883 
    884 			memset(&inbuf, 0, sizeof(inbuf));
    885 			memset(&req, 0, sizeof(req));
    886 
    887 			req.features = WDSM_STATUS;
    888 			req.command = WDCC_SMART;
    889 			req.cylinder = htole16(WDSMART_CYL);
    890 			req.timeout = 1000;
    891 
    892 			ata_command(&req);
    893 
    894 			if (req.cylinder != htole16(WDSMART_CYL)) {
    895 				fprintf(stderr, "Threshold exceeds condition\n");
    896 			}
    897 
    898 			/* WDSM_RD_DATA and WDSM_RD_THRESHOLDS are optional
    899 			 * features, the following ata_command()'s may error
    900 			 * and exit().
    901 			 */
    902 
    903 			memset(&inbuf, 0, sizeof(inbuf));
    904 			memset(&req, 0, sizeof(req));
    905 
    906 			req.flags = ATACMD_READ;
    907 			req.features = WDSM_RD_DATA;
    908 			req.command = WDCC_SMART;
    909 			req.databuf = (caddr_t) inbuf;
    910 			req.datalen = sizeof(inbuf);
    911 			req.cylinder = htole16(WDSMART_CYL);
    912 			req.timeout = 1000;
    913 
    914 			ata_command(&req);
    915 
    916 			memset(&inbuf2, 0, sizeof(inbuf2));
    917 			memset(&req, 0, sizeof(req));
    918 
    919 			req.flags = ATACMD_READ;
    920 			req.features = WDSM_RD_THRESHOLDS;
    921 			req.command = WDCC_SMART;
    922 			req.databuf = (caddr_t) inbuf2;
    923 			req.datalen = sizeof(inbuf2);
    924 			req.cylinder = htole16(WDSMART_CYL);
    925 			req.timeout = 1000;
    926 
    927 			ata_command(&req);
    928 
    929 			print_smart_status(inbuf, inbuf2);
    930 
    931 	} else if (strcmp(argv[0], "selftest-log") == 0) {
    932 		if (!is_smart()) {
    933 			fprintf(stderr, "SMART not supported\n");
    934 			return;
    935 		}
    936 
    937 		memset(&inbuf, 0, sizeof(inbuf));
    938 		memset(&req, 0, sizeof(req));
    939 
    940 		req.flags = ATACMD_READ;
    941 		req.features = WDSM_RD_LOG;
    942 		req.sec_count = 1;
    943 		req.sec_num = 6;
    944 		req.command = WDCC_SMART;
    945 		req.databuf = (caddr_t) inbuf;
    946 		req.datalen = sizeof(inbuf);
    947 		req.cylinder = htole16(WDSMART_CYL);
    948 		req.timeout = 1000;
    949 
    950 		ata_command(&req);
    951 
    952 		print_selftest(inbuf);
    953 
    954 	} else {
    955 		usage();
    956 	}
    957 	return;
    958 }
    959