xref: /netbsd-src/sbin/atactl/atactl.c (revision f82d7874c259b2a6cc59b714f844919f32bf7b51)
1 /*	$NetBSD: atactl.c,v 1.50 2008/04/28 20:23:07 martin 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  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * atactl(8) - a program to control ATA devices.
34  */
35 #include <sys/cdefs.h>
36 
37 #ifndef lint
38 __RCSID("$NetBSD: atactl.c,v 1.50 2008/04/28 20:23:07 martin Exp $");
39 #endif
40 
41 
42 #include <sys/param.h>
43 #include <sys/ioctl.h>
44 #include <err.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 #include <util.h>
52 
53 #include <dev/ata/atareg.h>
54 #include <sys/ataio.h>
55 
56 struct ata_smart_error {
57 	struct {
58 		u_int8_t device_control;
59 		u_int8_t features;
60 		u_int8_t sector_count;
61 		u_int8_t sector_number;
62 		u_int8_t cylinder_low;
63 		u_int8_t cylinder_high;
64 		u_int8_t device_head;
65 		u_int8_t command;
66 		u_int8_t timestamp[4];
67 	} command[5];
68 	struct {
69 		u_int8_t reserved;
70 		u_int8_t error;
71 		u_int8_t sector_count;
72 		u_int8_t sector_number;
73 		u_int8_t cylinder_low;
74 		u_int8_t cylinder_high;
75 		u_int8_t device_head;
76 		u_int8_t status;
77 		u_int8_t extended_error[19];
78 		u_int8_t state;
79 		u_int8_t lifetime[2];
80 	} error_data;
81 } __packed;
82 
83 struct ata_smart_errorlog {
84 	u_int8_t		data_structure_revision;
85 	u_int8_t		mostrecenterror;
86 	struct ata_smart_error	log_entries[5];
87 	u_int16_t		device_error_count;
88 	u_int8_t		reserved[57];
89 	u_int8_t		checksum;
90 } __packed;
91 
92 struct command {
93 	const char *cmd_name;
94 	const char *arg_names;
95 	void (*cmd_func)(int, char *[]);
96 };
97 
98 struct bitinfo {
99 	u_int bitmask;
100 	const char *string;
101 };
102 
103 void	usage(void);
104 void	ata_command(struct atareq *);
105 void	print_bitinfo(const char *, const char *, u_int, struct bitinfo *);
106 void	print_bitinfo2(const char *, const char *, u_int, u_int, struct bitinfo *);
107 void	print_smart_status(void *, void *);
108 void	print_error_entry(int, struct ata_smart_error *);
109 void	print_selftest_entry(int, struct ata_smart_selftest *);
110 
111 void	print_error(void *);
112 void	print_selftest(void *);
113 
114 struct ataparams *getataparams(void);
115 
116 int	is_smart(void);
117 
118 int	fd;				/* file descriptor for device */
119 const	char *dvname;			/* device name */
120 char	dvname_store[MAXPATHLEN];	/* for opendisk(3) */
121 const	char *cmdname;			/* command user issued */
122 const	char *argnames;			/* helpstring: expected arguments */
123 
124 void	device_identify(int, char *[]);
125 void	device_setidle(int, char *[]);
126 void	device_idle(int, char *[]);
127 void	device_apm(int, char *[]);
128 void	device_checkpower(int, char *[]);
129 void	device_smart(int, char *[]);
130 void	device_security(int, char *[]);
131 
132 void	device_smart_temp(struct ata_smart_attr *, uint64_t);
133 
134 struct command device_commands[] = {
135 	{ "identify",	"",			device_identify },
136 	{ "setidle",	"idle-timer",		device_setidle },
137 	{ "apm",	"disable|set #",	device_apm },
138 	{ "setstandby",	"standby-timer",	device_setidle },
139 	{ "idle",	"",			device_idle },
140 	{ "standby",	"",			device_idle },
141 	{ "sleep",	"",			device_idle },
142 	{ "checkpower",	"",			device_checkpower },
143 	{ "smart",	"enable|disable|status|offline #|error-log|selftest-log",
144 						device_smart },
145 	{ "security",	"freeze|status",	device_security },
146 	{ NULL,		NULL,			NULL },
147 };
148 
149 void	bus_reset(int, char *[]);
150 
151 struct command bus_commands[] = {
152 	{ "reset",	"",			bus_reset },
153 	{ NULL,		NULL,			NULL },
154 };
155 
156 /*
157  * Tables containing bitmasks used for error reporting and
158  * device identification.
159  */
160 
161 struct bitinfo ata_caps[] = {
162 	{ WDC_CAP_DMA, "DMA" },
163 	{ WDC_CAP_LBA, "LBA" },
164 	{ ATA_CAP_STBY, "ATA standby timer values" },
165 	{ WDC_CAP_IORDY, "IORDY operation" },
166 	{ WDC_CAP_IORDY_DSBL, "IORDY disabling" },
167 	{ 0, NULL },
168 };
169 
170 struct bitinfo ata_vers[] = {
171 	{ WDC_VER_ATA1,	"ATA-1" },
172 	{ WDC_VER_ATA2,	"ATA-2" },
173 	{ WDC_VER_ATA3,	"ATA-3" },
174 	{ WDC_VER_ATA4,	"ATA-4" },
175 	{ WDC_VER_ATA5,	"ATA-5" },
176 	{ WDC_VER_ATA6,	"ATA-6" },
177 	{ WDC_VER_ATA7,	"ATA-7" },
178 	{ 0, NULL },
179 };
180 
181 struct bitinfo ata_cmd_set1[] = {
182 	{ WDC_CMD1_NOP, "NOP command" },
183 	{ WDC_CMD1_RB, "READ BUFFER command" },
184 	{ WDC_CMD1_WB, "WRITE BUFFER command" },
185 	{ WDC_CMD1_HPA, "Host Protected Area feature set" },
186 	{ WDC_CMD1_DVRST, "DEVICE RESET command" },
187 	{ WDC_CMD1_SRV, "SERVICE interrupt" },
188 	{ WDC_CMD1_RLSE, "release interrupt" },
189 	{ WDC_CMD1_AHEAD, "look-ahead" },
190 	{ WDC_CMD1_CACHE, "write cache" },
191 	{ WDC_CMD1_PKT, "PACKET command feature set" },
192 	{ WDC_CMD1_PM, "Power Management feature set" },
193 	{ WDC_CMD1_REMOV, "Removable Media feature set" },
194 	{ WDC_CMD1_SEC, "Security Mode feature set" },
195 	{ WDC_CMD1_SMART, "SMART feature set" },
196 	{ 0, NULL },
197 };
198 
199 struct bitinfo ata_cmd_set2[] = {
200 	{ ATA_CMD2_FCE, "FLUSH CACHE EXT command" },
201 	{ WDC_CMD2_FC, "FLUSH CACHE command" },
202 	{ WDC_CMD2_DCO, "Device Configuration Overlay feature set" },
203 	{ ATA_CMD2_LBA48, "48-bit Address feature set" },
204 	{ WDC_CMD2_AAM, "Automatic Acoustic Management feature set" },
205 	{ WDC_CMD2_SM, "SET MAX security extension" },
206 	{ WDC_CMD2_SFREQ, "SET FEATURES required to spin-up after power-up" },
207 	{ WDC_CMD2_PUIS, "Power-Up In Standby feature set" },
208 	{ WDC_CMD2_RMSN, "Removable Media Status Notification feature set" },
209 	{ ATA_CMD2_APM, "Advanced Power Management feature set" },
210 	{ ATA_CMD2_CFA, "CFA feature set" },
211 	{ ATA_CMD2_RWQ, "READ/WRITE DMA QUEUED commands" },
212 	{ WDC_CMD2_DM, "DOWNLOAD MICROCODE command" },
213 	{ 0, NULL },
214 };
215 
216 struct bitinfo ata_cmd_ext[] = {
217 	{ ATA_CMDE_TLCONT, "Time-limited R/W feature set R/W Continuous mode" },
218 	{ ATA_CMDE_TL, "Time-limited Read/Write" },
219 	{ ATA_CMDE_URGW, "URG bit for WRITE STREAM DMA/PIO" },
220 	{ ATA_CMDE_URGR, "URG bit for READ STREAM DMA/PIO" },
221 	{ ATA_CMDE_WWN, "World Wide name" },
222 	{ ATA_CMDE_WQFE, "WRITE DMA QUEUED FUA EXT command" },
223 	{ ATA_CMDE_WFE, "WRITE DMA/MULTIPLE FUA EXT commands" },
224 	{ ATA_CMDE_GPL, "General Purpose Logging feature set" },
225 	{ ATA_CMDE_STREAM, "Streaming feature set" },
226 	{ ATA_CMDE_MCPTC, "Media Card Pass Through Command feature set" },
227 	{ ATA_CMDE_MS, "Media serial number" },
228 	{ ATA_CMDE_SST, "SMART self-test" },
229 	{ ATA_CMDE_SEL, "SMART error logging" },
230 	{ 0, NULL },
231 };
232 
233 struct bitinfo ata_sata_caps[] = {
234 	{ SATA_SIGNAL_GEN1, "1.5Gb/s signaling" },
235 	{ SATA_SIGNAL_GEN2, "3.0Gb/s signaling" },
236 	{ SATA_NATIVE_CMDQ, "Native Command Queuing" },
237 	{ SATA_HOST_PWR_MGMT, "Host-Initiated Interface Power Management" },
238 	{ SATA_PHY_EVNT_CNT, "PHY Event Counters" },
239 	{ 0, NULL },
240 };
241 
242 struct bitinfo ata_sata_feat[] = {
243 	{ SATA_NONZERO_OFFSETS, "Non-zero Offset DMA" },
244 	{ SATA_DMA_SETUP_AUTO, "DMA Setup Auto Activate" },
245 	{ SATA_DRIVE_PWR_MGMT, "Device-Initiated Interface Power Managment" },
246 	{ SATA_IN_ORDER_DATA, "In-order Data Delivery" },
247 	{ SATA_SW_STTNGS_PRS, "Software Settings Preservation" },
248 	{ 0, NULL },
249 };
250 
251 static const struct {
252 	const int	id;
253 	const char	*name;
254 	void (*special)(struct ata_smart_attr *, uint64_t);
255 } smart_attrs[] = {
256 	{   1,		"Raw read error rate", NULL },
257 	{   2,		"Throughput performance", NULL },
258 	{   3,		"Spin-up time", NULL },
259 	{   4,		"Start/stop count", NULL },
260 	{   5,		"Reallocated sector count", NULL },
261 	{   6,		"Read channel margin", NULL },
262 	{   7,		"Seek error rate", NULL },
263 	{   8,		"Seek time performance", NULL },
264 	{   9,		"Power-on hours count", NULL },
265 	{  10,		"Spin retry count", NULL },
266 	{  11,		"Calibration retry count", NULL },
267 	{  12,		"Device power cycle count", NULL },
268 	{ 191,		"Gsense error rate", NULL },
269 	{ 192,		"Power-off retract count", NULL },
270 	{ 193,		"Load cycle count", NULL },
271 	{ 194,		"Temperature",			device_smart_temp},
272 	{ 195,		"Hardware ECC Recovered", NULL },
273 	{ 196,		"Reallocated event count", NULL },
274 	{ 197,		"Current pending sector", NULL },
275 	{ 198,		"Offline uncorrectable", NULL },
276 	{ 199,		"Ultra DMA CRC error count", NULL },
277 	{ 200,		"Write error rate", NULL },
278 	{ 201,		"Soft read error rate", NULL },
279 	{ 202,		"Data address mark errors", NULL },
280 	{ 203,		"Run out cancel", NULL },
281 	{ 204,		"Soft ECC correction", NULL },
282 	{ 205,		"Thermal asperity check", NULL },
283 	{ 206,		"Flying height", NULL },
284 	{ 207,		"Spin high current", NULL },
285 	{ 208,		"Spin buzz", NULL },
286 	{ 209,		"Offline seek performance", NULL },
287 	{ 220,		"Disk shift", NULL },
288 	{ 221,		"G-Sense error rate", NULL },
289 	{ 222,		"Loaded hours", NULL },
290 	{ 223,		"Load/unload retry count", NULL },
291 	{ 224,		"Load friction", NULL },
292 	{ 225,		"Load/unload cycle count", NULL },
293 	{ 226,		"Load-in time", NULL },
294 	{ 227,		"Torque amplification count", NULL },
295 	{ 228,		"Power-off retract count", NULL },
296 	{ 230,		"GMR head amplitude", NULL },
297 	{ 231,		"Temperature",			device_smart_temp },
298 	{ 240,		"Head flying hours", NULL },
299 	{ 250,		"Read error retry rate", NULL },
300 	{   0,		"Unknown", NULL },
301 };
302 
303 struct bitinfo ata_sec_st[] = {
304 	{ WDC_SEC_SUPP,		"supported" },
305 	{ WDC_SEC_EN,		"enabled" },
306 	{ WDC_SEC_LOCKED,	"locked" },
307 	{ WDC_SEC_FROZEN,	"frozen" },
308 	{ WDC_SEC_EXP,		"expired" },
309 	{ WDC_SEC_ESE_SUPP,	"enhanced erase support" },
310 	{ WDC_SEC_LEV_MAX,	"maximum level" },
311 	{ 0,			NULL },
312 };
313 
314 int
315 main(int argc, char *argv[])
316 {
317 	int i;
318 	struct command *commands = NULL;
319 
320 	/* Must have at least: device command */
321 	if (argc < 3)
322 		usage();
323 
324 	/* Skip program name, get and skip device name and command. */
325 	dvname = argv[1];
326 	cmdname = argv[2];
327 	argv += 3;
328 	argc -= 3;
329 
330 	/*
331 	 * Open the device
332 	 */
333 	fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0);
334 	if (fd == -1) {
335 		if (errno == ENOENT) {
336 			/*
337 			 * Device doesn't exist.  Probably trying to open
338 			 * a device which doesn't use disk semantics for
339 			 * device name.  Try again, specifying "cooked",
340 			 * which leaves off the "r" in front of the device's
341 			 * name.
342 			 */
343 			fd = opendisk(dvname, O_RDWR, dvname_store,
344 			    sizeof(dvname_store), 1);
345 			if (fd == -1)
346 				err(1, "%s", dvname);
347 		} else
348 			err(1, "%s", dvname);
349 	}
350 
351 	/*
352 	 * Point the dvname at the actual device name that opendisk() opened.
353 	 */
354 	dvname = dvname_store;
355 
356 	/* Look up and call the command. */
357 	for (i = 0; device_commands[i].cmd_name != NULL; i++) {
358 		if (strcmp(cmdname, device_commands[i].cmd_name) == 0) {
359 			commands = &device_commands[i];
360 			break;
361 		}
362 	}
363 	if (commands == NULL) {
364 		for (i = 0; bus_commands[i].cmd_name != NULL; i++) {
365 			if (strcmp(cmdname, bus_commands[i].cmd_name) == 0) {
366 				commands = &bus_commands[i];
367 				break;
368 			}
369 		}
370 	}
371 	if (commands == NULL)
372 		errx(1, "unknown command: %s", cmdname);
373 
374 	argnames = commands->arg_names;
375 
376 	(*commands->cmd_func)(argc, argv);
377 	exit(0);
378 }
379 
380 void
381 usage(void)
382 {
383 	int i;
384 
385 	fprintf(stderr, "usage: %s device command [arg [...]]\n",
386 	    getprogname());
387 
388 	fprintf(stderr, "   Available device commands:\n");
389 	for (i=0; device_commands[i].cmd_name != NULL; i++)
390 		fprintf(stderr, "\t%s %s\n", device_commands[i].cmd_name,
391 					    device_commands[i].arg_names);
392 
393 	fprintf(stderr, "   Available bus commands:\n");
394 	for (i=0; bus_commands[i].cmd_name != NULL; i++)
395 		fprintf(stderr, "\t%s %s\n", bus_commands[i].cmd_name,
396 					    bus_commands[i].arg_names);
397 
398 	exit(1);
399 }
400 
401 /*
402  * Wrapper that calls ATAIOCCOMMAND and checks for errors
403  */
404 
405 void
406 ata_command(struct atareq *req)
407 {
408 	int error;
409 
410 	error = ioctl(fd, ATAIOCCOMMAND, req);
411 
412 	if (error == -1)
413 		err(1, "ATAIOCCOMMAND failed");
414 
415 	switch (req->retsts) {
416 
417 	case ATACMD_OK:
418 		return;
419 	case ATACMD_TIMEOUT:
420 		fprintf(stderr, "ATA command timed out\n");
421 		exit(1);
422 	case ATACMD_DF:
423 		fprintf(stderr, "ATA device returned a Device Fault\n");
424 		exit(1);
425 	case ATACMD_ERROR:
426 		if (req->error & WDCE_ABRT)
427 			fprintf(stderr, "ATA device returned Aborted "
428 				"Command\n");
429 		else
430 			fprintf(stderr, "ATA device returned error register "
431 				"%0x\n", req->error);
432 		exit(1);
433 	default:
434 		fprintf(stderr, "ATAIOCCOMMAND returned unknown result code "
435 			"%d\n", req->retsts);
436 		exit(1);
437 	}
438 }
439 
440 /*
441  * Print out strings associated with particular bitmasks
442  */
443 
444 void
445 print_bitinfo(const char *bf, const char *af, u_int bits, struct bitinfo *binfo)
446 {
447 
448 	for (; binfo->bitmask != 0; binfo++)
449 		if (bits & binfo->bitmask)
450 			printf("%s%s%s", bf, binfo->string, af);
451 }
452 
453 void
454 print_bitinfo2(const char *bf, const char *af, u_int bits, u_int enables, struct bitinfo *binfo)
455 {
456 
457 	for (; binfo->bitmask != 0; binfo++)
458 		if (bits & binfo->bitmask)
459 			printf("%s%s (%s)%s", bf, binfo->string,
460 			    (enables & binfo->bitmask) ? "enabled" : "disabled",
461 			    af);
462 }
463 
464 
465 /*
466  * Try to print SMART temperature field
467  */
468 
469 void
470 device_smart_temp(struct ata_smart_attr *attr, uint64_t raw_value)
471 {
472 	printf("%" PRIu8, attr->raw[0]);
473 	if (attr->raw[0] != raw_value)
474 		printf(" Lifetime max/min %" PRIu8 "/%" PRIu8,
475 		    attr->raw[2], attr->raw[4]);
476 }
477 
478 
479 /*
480  * Print out SMART attribute thresholds and values
481  */
482 
483 void
484 print_smart_status(void *vbuf, void *tbuf)
485 {
486 	struct ata_smart_attributes *value_buf = vbuf;
487 	struct ata_smart_thresholds *threshold_buf = tbuf;
488 	struct ata_smart_attr *attr;
489 	uint64_t raw_value;
490 	int flags;
491 	int i, j;
492 	int aid;
493 	u_int8_t checksum;
494 
495 	for (i = checksum = 0; i < 512; i++)
496 		checksum += ((u_int8_t *) value_buf)[i];
497 	if (checksum != 0) {
498 		fprintf(stderr, "SMART attribute values checksum error\n");
499 		return;
500 	}
501 
502 	for (i = checksum = 0; i < 512; i++)
503 		checksum += ((u_int8_t *) threshold_buf)[i];
504 	if (checksum != 0) {
505 		fprintf(stderr, "SMART attribute thresholds checksum error\n");
506 		return;
507 	}
508 
509 	printf("id value thresh crit collect reliability description\t\t\traw\n");
510 	for (i = 0; i < 256; i++) {
511 		int thresh = 0;
512 
513 		attr = NULL;
514 
515 		for (j = 0; j < 30; j++) {
516 			if (value_buf->attributes[j].id == i)
517 				attr = &value_buf->attributes[j];
518 			if (threshold_buf->thresholds[j].id == i)
519 				thresh = threshold_buf->thresholds[j].value;
520 		}
521 
522 		if (thresh && attr == NULL)
523 			errx(1, "threshold but not attr %d", i);
524 		if (attr == NULL)
525 			continue;
526 
527 		if (attr->value == 0||attr->value == 0xFE||attr->value == 0xFF)
528 			continue;
529 
530 		for (aid = 0;
531 		     smart_attrs[aid].id != i && smart_attrs[aid].id != 0;
532 		     aid++)
533 			;
534 
535 		flags = le16toh(attr->flags);
536 
537 		printf("%3d %3d  %3d     %-3s %-7s %stive    %-24s\t",
538 		    i, attr->value, thresh,
539 		    flags & WDSM_ATTR_ADVISORY ? "yes" : "no",
540 		    flags & WDSM_ATTR_COLLECTIVE ? "online" : "offline",
541 		    attr->value > thresh ? "posi" : "nega",
542 		    smart_attrs[aid].name);
543 
544 		for (j = 0, raw_value = 0; j < 6; j++)
545 			raw_value += ((uint64_t)attr->raw[j]) << (8*j);
546 
547 		if (smart_attrs[aid].special)
548 			(*smart_attrs[aid].special)(attr, raw_value);
549 		else
550 			printf("%" PRIu64, raw_value);
551 		printf("\n");
552 		}
553 	}
554 
555 struct {
556 	int number;
557 	const char *name;
558 } selftest_name[] = {
559 	{ 0, "Off-line" },
560 	{ 1, "Short off-line" },
561 	{ 2, "Extended off-line" },
562 	{ 127, "Abort off-line test" },
563 	{ 129, "Short captive" },
564 	{ 130, "Extended captive" },
565 	{ 256, "Unknown test" }, /* larger then u_int8_t */
566 	{ 0, NULL }
567 };
568 
569 const char *selftest_status[] = {
570 	"No error",
571 	"Aborted by the host",
572 	"Interrupted by the host by reset",
573 	"Fatal error or unknown test error",
574 	"Unknown test element failed",
575 	"Electrical test element failed",
576 	"The Servo (and/or seek) test element failed",
577 	"Read element of test failed",
578 	"Reserved",
579 	"Reserved",
580 	"Reserved",
581 	"Reserved",
582 	"Reserved",
583 	"Reserved",
584 	"Reserved",
585 	"Self-test in progress"
586 };
587 
588 void
589 print_error_entry(int num, struct ata_smart_error *le)
590 {
591 	int i;
592 
593 	printf("Log entry: %d\n", num);
594 
595 	for (i = 0; i < 5; i++)
596 		printf("\tCommand %d: dc=%02x sf=%02x sc=%02x sn=%02x cl=%02x ch=%02x dh=%02x cmd=%02x time=%02x%02x%02x%02x\n", i,
597 		    le->command[i].device_control,
598 		    le->command[i].features,
599 		    le->command[i].sector_count,
600 		    le->command[i].sector_number,
601 		    le->command[i].cylinder_low,
602 		    le->command[i].cylinder_high,
603 		    le->command[i].device_head,
604 		    le->command[i].command,
605 		    le->command[i].timestamp[3],
606 		    le->command[i].timestamp[2],
607 		    le->command[i].timestamp[1],
608 		    le->command[i].timestamp[0]);
609 	printf("\tError: err=%02x sc=%02x sn=%02x cl=%02x ch=%02x dh=%02x status=%02x state=%02x lifetime=%02x%02x\n",
610 	    le->error_data.error,
611 	    le->error_data.sector_count,
612 	    le->error_data.sector_number,
613 	    le->error_data.cylinder_low,
614 	    le->error_data.cylinder_high,
615 	    le->error_data.device_head,
616 	    le->error_data.status,
617 	    le->error_data.state,
618 	    le->error_data.lifetime[1],
619 	    le->error_data.lifetime[0]);
620 	printf("\tExtended: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
621 	    le->error_data.extended_error[0],
622 	    le->error_data.extended_error[1],
623 	    le->error_data.extended_error[2],
624 	    le->error_data.extended_error[3],
625 	    le->error_data.extended_error[4],
626 	    le->error_data.extended_error[5],
627 	    le->error_data.extended_error[6],
628 	    le->error_data.extended_error[7],
629 	    le->error_data.extended_error[8],
630 	    le->error_data.extended_error[9],
631 	    le->error_data.extended_error[10],
632 	    le->error_data.extended_error[11],
633 	    le->error_data.extended_error[12],
634 	    le->error_data.extended_error[13],
635 	    le->error_data.extended_error[14],
636 	    le->error_data.extended_error[15],
637 	    le->error_data.extended_error[15],
638 	    le->error_data.extended_error[17],
639 	    le->error_data.extended_error[18]);
640 }
641 
642 void
643 print_error(void *buf)
644 {
645 	struct ata_smart_errorlog *erlog = buf;
646 	u_int8_t checksum;
647 	int i;
648 
649 	for (i = checksum = 0; i < 512; i++)
650 		checksum += ((u_int8_t *) buf)[i];
651 	if (checksum != 0) {
652 		fprintf(stderr, "SMART error log checksum error\n");
653 		return;
654 	}
655 
656 	if (erlog->data_structure_revision != 1) {
657 		fprintf(stderr, "Error log revision not 1 (found 0x%04x)\n",
658 		    erlog->data_structure_revision);
659 		return;
660 	}
661 
662 	if (erlog->mostrecenterror == 0) {
663 		printf("No errors have been logged\n");
664 		return;
665 	}
666 
667 	if (erlog->mostrecenterror > 5) {
668 		fprintf(stderr, "Most recent error is too large\n");
669 		return;
670 	}
671 
672 	for (i = erlog->mostrecenterror; i < 5; i++)
673 		print_error_entry(i, &erlog->log_entries[i]);
674 	for (i = 0; i < erlog->mostrecenterror; i++)
675 		print_error_entry(i, &erlog->log_entries[i]);
676 	printf("device error count: %d\n", erlog->device_error_count);
677 }
678 
679 void
680 print_selftest_entry(int num, struct ata_smart_selftest *le)
681 {
682 	unsigned char *p;
683 	int i;
684 
685 	/* check if all zero */
686 	for (p = (void *)le, i = 0; i < sizeof(*le); i++)
687 		if (p[i] != 0)
688 			break;
689 	if (i == sizeof(*le))
690 		return;
691 
692 	printf("Log entry: %d\n", num);
693 
694 	/* Get test name */
695 	for (i = 0; selftest_name[i].name != NULL; i++)
696 		if (selftest_name[i].number == le->number)
697 			break;
698 
699 	if (selftest_name[i].name == NULL)
700 		printf("\tName: (%d)\n", le->number);
701 	else
702 		printf("\tName: %s\n", selftest_name[i].name);
703 	printf("\tStatus: %s\n", selftest_status[le->status >> 4]);
704 	/* XXX This generally should not be set when a self-test is completed,
705 	   and at any rate is useless.  - mycroft */
706 	if (le->status >> 4 == 15)
707 		printf("\tPercent of test remaining: %1d0\n", le->status & 0xf);
708 	else if (le->status >> 4 != 0)
709 		printf("\tLBA first error: %d\n", le32toh(le->lba_first_error));
710 }
711 
712 void
713 print_selftest(void *buf)
714 {
715 	struct ata_smart_selftestlog *stlog = buf;
716 	u_int8_t checksum;
717 	int i;
718 
719 	for (i = checksum = 0; i < 512; i++)
720 		checksum += ((u_int8_t *) buf)[i];
721 	if (checksum != 0) {
722 		fprintf(stderr, "SMART selftest log checksum error\n");
723 		return;
724 	}
725 
726 	if (le16toh(stlog->data_structure_revision) != 1) {
727 		fprintf(stderr, "Self-test log revision not 1 (found 0x%04x)\n",
728 		    le16toh(stlog->data_structure_revision));
729 		return;
730 	}
731 
732 	if (stlog->mostrecenttest == 0) {
733 		printf("No self-tests have been logged\n");
734 		return;
735 	}
736 
737 	if (stlog->mostrecenttest > 22) {
738 		fprintf(stderr, "Most recent test is too large\n");
739 		return;
740 	}
741 
742 	for (i = stlog->mostrecenttest; i < 22; i++)
743 		print_selftest_entry(i, &stlog->log_entries[i]);
744 	for (i = 0; i < stlog->mostrecenttest; i++)
745 		print_selftest_entry(i, &stlog->log_entries[i]);
746 }
747 
748 struct ataparams *
749 getataparams()
750 {
751 	struct atareq req;
752 	static union {
753 		unsigned char inbuf[DEV_BSIZE];
754 		struct ataparams inqbuf;
755 	} inbuf;
756 
757 	memset(&inbuf, 0, sizeof(inbuf));
758 	memset(&req, 0, sizeof(req));
759 
760 	req.flags = ATACMD_READ;
761 	req.command = WDCC_IDENTIFY;
762 	req.databuf = (caddr_t)&inbuf;
763 	req.datalen = sizeof(inbuf);
764 	req.timeout = 1000;
765 
766 	ata_command(&req);
767 
768 	return (&inbuf.inqbuf);
769 }
770 
771 /*
772  * is_smart:
773  *
774  *	Detect whether device supports SMART and SMART is enabled.
775  */
776 
777 int
778 is_smart(void)
779 {
780 	int retval = 0;
781 	struct ataparams *inqbuf;
782 	const char *status;
783 
784 	inqbuf = getataparams();
785 
786 	if (inqbuf->atap_cmd_def != 0 && inqbuf->atap_cmd_def != 0xffff) {
787 		if (!(inqbuf->atap_cmd_set1 & WDC_CMD1_SMART)) {
788 			fprintf(stderr, "SMART unsupported\n");
789 		} else {
790 			if (inqbuf->atap_ata_major <= WDC_VER_ATA5 ||
791 			    inqbuf->atap_cmd_set2 == 0xffff ||
792 			    inqbuf->atap_cmd_set2 == 0x0000) {
793 				status = "status unknown";
794 				retval = 2;
795 			} else {
796 				if (inqbuf->atap_cmd1_en & WDC_CMD1_SMART) {
797 					status = "enabled";
798 					retval = 1;
799 				} else {
800 					status = "disabled";
801 					retval = 3;
802 				}
803 			}
804 			printf("SMART supported, SMART %s\n", status);
805 		}
806 	}
807 	return retval;
808 }
809 
810 /*
811  * DEVICE COMMANDS
812  */
813 
814 /*
815  * device_identify:
816  *
817  *	Display the identity of the device
818  */
819 void
820 device_identify(int argc, char *argv[])
821 {
822 	struct ataparams *inqbuf;
823 #if BYTE_ORDER == LITTLE_ENDIAN
824 	int i;
825 	u_int16_t *p;
826 #endif
827 
828 	/* No arguments. */
829 	if (argc != 0)
830 		usage();
831 
832 	inqbuf = getataparams();
833 
834 #if BYTE_ORDER == LITTLE_ENDIAN
835 	/*
836 	 * On little endian machines, we need to shuffle the string
837 	 * byte order.  However, we don't have to do this for NEC or
838 	 * Mitsumi ATAPI devices
839 	 */
840 
841 	if (!((inqbuf->atap_config & WDC_CFG_ATAPI_MASK) == WDC_CFG_ATAPI &&
842 	      ((inqbuf->atap_model[0] == 'N' &&
843 		  inqbuf->atap_model[1] == 'E') ||
844 	       (inqbuf->atap_model[0] == 'F' &&
845 		  inqbuf->atap_model[1] == 'X')))) {
846 		for (i = 0 ; i < sizeof(inqbuf->atap_model); i += 2) {
847 			p = (u_short *) (inqbuf->atap_model + i);
848 			*p = ntohs(*p);
849 		}
850 		for (i = 0 ; i < sizeof(inqbuf->atap_serial); i += 2) {
851 			p = (u_short *) (inqbuf->atap_serial + i);
852 			*p = ntohs(*p);
853 		}
854 		for (i = 0 ; i < sizeof(inqbuf->atap_revision); i += 2) {
855 			p = (u_short *) (inqbuf->atap_revision + i);
856 			*p = ntohs(*p);
857 		}
858 	}
859 #endif
860 
861 	/*
862 	 * Strip blanks off of the info strings.  Yuck, I wish this was
863 	 * cleaner.
864 	 */
865 
866 	if (inqbuf->atap_model[sizeof(inqbuf->atap_model) - 1] == ' ') {
867 		inqbuf->atap_model[sizeof(inqbuf->atap_model) - 1] = '\0';
868 		while (inqbuf->atap_model[strlen(inqbuf->atap_model) - 1] == ' ')
869 			inqbuf->atap_model[strlen(inqbuf->atap_model) - 1] = '\0';
870 	}
871 
872 	if (inqbuf->atap_revision[sizeof(inqbuf->atap_revision) - 1] == ' ') {
873 		inqbuf->atap_revision[sizeof(inqbuf->atap_revision) - 1] = '\0';
874 		while (inqbuf->atap_revision[strlen(inqbuf->atap_revision) - 1] == ' ')
875 			inqbuf->atap_revision[strlen(inqbuf->atap_revision) - 1] = '\0';
876 	}
877 
878 	if (inqbuf->atap_serial[sizeof(inqbuf->atap_serial) - 1] == ' ') {
879 		inqbuf->atap_serial[sizeof(inqbuf->atap_serial) - 1] = '\0';
880 		while (inqbuf->atap_serial[strlen(inqbuf->atap_serial) - 1] == ' ')
881 			inqbuf->atap_serial[strlen(inqbuf->atap_serial) - 1] = '\0';
882 	}
883 
884 	printf("Model: %.*s, Rev: %.*s, Serial #: %.*s\n",
885 	       (int) sizeof(inqbuf->atap_model), inqbuf->atap_model,
886 	       (int) sizeof(inqbuf->atap_revision), inqbuf->atap_revision,
887 	       (int) sizeof(inqbuf->atap_serial), inqbuf->atap_serial);
888 
889 	printf("Device type: %s, %s\n", inqbuf->atap_config & WDC_CFG_ATAPI ?
890 	       "ATAPI" : "ATA", inqbuf->atap_config & ATA_CFG_FIXED ? "fixed" :
891 	       "removable");
892 
893 	if ((inqbuf->atap_config & WDC_CFG_ATAPI_MASK) == 0)
894 		printf("Cylinders: %d, heads: %d, sec/track: %d, total "
895 		       "sectors: %d\n", inqbuf->atap_cylinders,
896 		       inqbuf->atap_heads, inqbuf->atap_sectors,
897 		       (inqbuf->atap_capacity[1] << 16) |
898 		       inqbuf->atap_capacity[0]);
899 
900 	if (inqbuf->atap_queuedepth & WDC_QUEUE_DEPTH_MASK)
901 		printf("Device supports command queue depth of %d\n",
902 		       inqbuf->atap_queuedepth & WDC_QUEUE_DEPTH_MASK);
903 
904 	printf("Device capabilities:\n");
905 	print_bitinfo("\t", "\n", inqbuf->atap_capabilities1, ata_caps);
906 
907 	if (inqbuf->atap_ata_major != 0 && inqbuf->atap_ata_major != 0xffff) {
908 		printf("Device supports following standards:\n");
909 		print_bitinfo("", " ", inqbuf->atap_ata_major, ata_vers);
910 		printf("\n");
911 	}
912 
913 	if (inqbuf->atap_cmd_set1 != 0 && inqbuf->atap_cmd_set1 != 0xffff &&
914 	    inqbuf->atap_cmd_set2 != 0 && inqbuf->atap_cmd_set2 != 0xffff) {
915 		printf("Command set support:\n");
916 		if (inqbuf->atap_cmd1_en != 0 && inqbuf->atap_cmd1_en != 0xffff)
917 			print_bitinfo2("\t", "\n", inqbuf->atap_cmd_set1,
918 			    inqbuf->atap_cmd1_en, ata_cmd_set1);
919 		else
920 			print_bitinfo("\t", "\n", inqbuf->atap_cmd_set1,
921 			    ata_cmd_set1);
922 		if (inqbuf->atap_cmd2_en != 0 && inqbuf->atap_cmd2_en != 0xffff)
923 			print_bitinfo2("\t", "\n", inqbuf->atap_cmd_set2,
924 			    inqbuf->atap_cmd2_en, ata_cmd_set2);
925 		else
926 			print_bitinfo("\t", "\n", inqbuf->atap_cmd_set2,
927 			    ata_cmd_set2);
928 		if (inqbuf->atap_cmd_ext != 0 && inqbuf->atap_cmd_ext != 0xffff)
929 			print_bitinfo("\t", "\n", inqbuf->atap_cmd_ext,
930 			    ata_cmd_ext);
931 	}
932 
933 	if (inqbuf->atap_sata_caps != 0 && inqbuf->atap_sata_caps != 0xffff) {
934 		printf("Serial ATA capabilities:\n");
935 		print_bitinfo("\t", "\n", inqbuf->atap_sata_caps, ata_sata_caps);
936 	}
937 
938 	if (inqbuf->atap_sata_features_supp != 0 && inqbuf->atap_sata_features_supp != 0xffff) {
939 		printf("Serial ATA features:\n");
940 		if (inqbuf->atap_sata_features_en != 0 && inqbuf->atap_sata_features_en != 0xffff)
941 			print_bitinfo2("\t", "\n", inqbuf->atap_sata_features_supp, inqbuf->atap_sata_features_en, ata_sata_feat);
942 		else
943 			print_bitinfo("\t", "\n", inqbuf->atap_sata_features_supp, ata_sata_feat);
944 	}
945 
946 	return;
947 }
948 
949 /*
950  * device idle:
951  *
952  * issue the IDLE IMMEDIATE command to the drive
953  */
954 void
955 device_idle(int argc, char *argv[])
956 {
957 	struct atareq req;
958 
959 	/* No arguments. */
960 	if (argc != 0)
961 		usage();
962 
963 	memset(&req, 0, sizeof(req));
964 
965 	if (strcmp(cmdname, "idle") == 0)
966 		req.command = WDCC_IDLE_IMMED;
967 	else if (strcmp(cmdname, "standby") == 0)
968 		req.command = WDCC_STANDBY_IMMED;
969 	else
970 		req.command = WDCC_SLEEP;
971 
972 	req.timeout = 1000;
973 
974 	ata_command(&req);
975 
976 	return;
977 }
978 
979 /*
980  * device apm:
981  *
982  * enable/disable/control the APM feature of the drive
983  */
984 void
985 device_apm(int argc, char *argv[])
986 {
987 	struct atareq req;
988 	long l;
989 
990 	memset(&req, 0, sizeof(req));
991 	if (argc >= 1) {
992 		req.command = SET_FEATURES;
993 		req.timeout = 1000;
994 
995 		if (strcmp(argv[0], "disable") == 0)
996 			req.features = WDSF_APM_DS;
997 		else if (strcmp(argv[0], "set") == 0 && argc >= 2 &&
998 		         (l = strtol(argv[1], NULL, 0)) >= 0 && l <= 253) {
999 
1000 			req.features = WDSF_APM_EN;
1001 			req.sec_count = l + 1;
1002 		} else
1003 			usage();
1004 	} else
1005 		usage();
1006 
1007 	ata_command(&req);
1008 }
1009 
1010 
1011 /*
1012  * Set the idle timer on the disk.  Set it for either idle mode or
1013  * standby mode, depending on how we were invoked.
1014  */
1015 
1016 void
1017 device_setidle(int argc, char *argv[])
1018 {
1019 	unsigned long idle;
1020 	struct atareq req;
1021 	char *end;
1022 
1023 	/* Only one argument */
1024 	if (argc != 1)
1025 		usage();
1026 
1027 	idle = strtoul(argv[0], &end, 0);
1028 
1029 	if (*end != '\0') {
1030 		fprintf(stderr, "Invalid idle time: \"%s\"\n", argv[0]);
1031 		exit(1);
1032 	}
1033 
1034 	if (idle > 19800) {
1035 		fprintf(stderr, "Idle time has a maximum value of 5.5 "
1036 			"hours\n");
1037 		exit(1);
1038 	}
1039 
1040 	if (idle != 0 && idle < 5) {
1041 		fprintf(stderr, "Idle timer must be at least 5 seconds\n");
1042 		exit(1);
1043 	}
1044 
1045 	memset(&req, 0, sizeof(req));
1046 
1047 	if (idle <= 240*5)
1048 		req.sec_count = idle / 5;
1049 	else
1050 		req.sec_count = idle / (30*60) + 240;
1051 
1052 	req.command = cmdname[3] == 's' ? WDCC_STANDBY : WDCC_IDLE;
1053 	req.timeout = 1000;
1054 
1055 	ata_command(&req);
1056 
1057 	return;
1058 }
1059 
1060 /*
1061  * Query the device for the current power mode
1062  */
1063 
1064 void
1065 device_checkpower(int argc, char *argv[])
1066 {
1067 	struct atareq req;
1068 
1069 	/* No arguments. */
1070 	if (argc != 0)
1071 		usage();
1072 
1073 	memset(&req, 0, sizeof(req));
1074 
1075 	req.command = WDCC_CHECK_PWR;
1076 	req.timeout = 1000;
1077 	req.flags = ATACMD_READREG;
1078 
1079 	ata_command(&req);
1080 
1081 	printf("Current power status: ");
1082 
1083 	switch (req.sec_count) {
1084 	case 0x00:
1085 		printf("Standby mode\n");
1086 		break;
1087 	case 0x80:
1088 		printf("Idle mode\n");
1089 		break;
1090 	case 0xff:
1091 		printf("Active mode\n");
1092 		break;
1093 	default:
1094 		printf("Unknown power code (%02x)\n", req.sec_count);
1095 	}
1096 
1097 	return;
1098 }
1099 
1100 /*
1101  * device_smart:
1102  *
1103  *	Display SMART status
1104  */
1105 void
1106 device_smart(int argc, char *argv[])
1107 {
1108 	struct atareq req;
1109 	unsigned char inbuf[DEV_BSIZE];
1110 	unsigned char inbuf2[DEV_BSIZE];
1111 
1112 	if (argc < 1)
1113 		usage();
1114 
1115 	if (strcmp(argv[0], "enable") == 0) {
1116 		memset(&req, 0, sizeof(req));
1117 
1118 		req.features = WDSM_ENABLE_OPS;
1119 		req.command = WDCC_SMART;
1120 		req.cylinder = WDSMART_CYL;
1121 		req.timeout = 1000;
1122 
1123 		ata_command(&req);
1124 
1125 		is_smart();
1126 	} else if (strcmp(argv[0], "disable") == 0) {
1127 		memset(&req, 0, sizeof(req));
1128 
1129 		req.features = WDSM_DISABLE_OPS;
1130 		req.command = WDCC_SMART;
1131 		req.cylinder = WDSMART_CYL;
1132 		req.timeout = 1000;
1133 
1134 		ata_command(&req);
1135 
1136 		is_smart();
1137 	} else if (strcmp(argv[0], "status") == 0) {
1138 		int rv;
1139 
1140 		rv = is_smart();
1141 
1142 		if (!rv) {
1143 			fprintf(stderr, "SMART not supported\n");
1144 			return;
1145 		} else if (rv == 3)
1146 			return;
1147 
1148 		memset(&inbuf, 0, sizeof(inbuf));
1149 		memset(&req, 0, sizeof(req));
1150 
1151 		req.features = WDSM_STATUS;
1152 		req.command = WDCC_SMART;
1153 		req.cylinder = WDSMART_CYL;
1154 		req.timeout = 1000;
1155 
1156 		ata_command(&req);
1157 
1158 		if (req.cylinder != WDSMART_CYL) {
1159 			fprintf(stderr, "Threshold exceeds condition\n");
1160 		}
1161 
1162 		/* WDSM_RD_DATA and WDSM_RD_THRESHOLDS are optional
1163 		 * features, the following ata_command()'s may error
1164 		 * and exit().
1165 		 */
1166 
1167 		memset(&inbuf, 0, sizeof(inbuf));
1168 		memset(&req, 0, sizeof(req));
1169 
1170 		req.flags = ATACMD_READ;
1171 		req.features = WDSM_RD_DATA;
1172 		req.command = WDCC_SMART;
1173 		req.databuf = (caddr_t) inbuf;
1174 		req.datalen = sizeof(inbuf);
1175 		req.cylinder = WDSMART_CYL;
1176 		req.timeout = 1000;
1177 
1178 		ata_command(&req);
1179 
1180 		memset(&inbuf2, 0, sizeof(inbuf2));
1181 		memset(&req, 0, sizeof(req));
1182 
1183 		req.flags = ATACMD_READ;
1184 		req.features = WDSM_RD_THRESHOLDS;
1185 		req.command = WDCC_SMART;
1186 		req.databuf = (caddr_t) inbuf2;
1187 		req.datalen = sizeof(inbuf2);
1188 		req.cylinder = WDSMART_CYL;
1189 		req.timeout = 1000;
1190 
1191 		ata_command(&req);
1192 
1193 		print_smart_status(inbuf, inbuf2);
1194 
1195 	} else if (strcmp(argv[0], "offline") == 0) {
1196 		if (argc != 2)
1197 			usage();
1198 		if (!is_smart()) {
1199 			fprintf(stderr, "SMART not supported\n");
1200 			return;
1201 		}
1202 
1203 		memset(&req, 0, sizeof(req));
1204 
1205 		req.features = WDSM_EXEC_OFFL_IMM;
1206 		req.command = WDCC_SMART;
1207 		req.cylinder = WDSMART_CYL;
1208 		req.sec_num = atol(argv[1]);
1209 		req.timeout = 10000;
1210 
1211 		ata_command(&req);
1212 	} else if (strcmp(argv[0], "error-log") == 0) {
1213 		if (!is_smart()) {
1214 			fprintf(stderr, "SMART not supported\n");
1215 			return;
1216 		}
1217 
1218 		memset(&inbuf, 0, sizeof(inbuf));
1219 		memset(&req, 0, sizeof(req));
1220 
1221 		req.flags = ATACMD_READ;
1222 		req.features = WDSM_RD_LOG;
1223 		req.sec_count = 1;
1224 		req.sec_num = 1;
1225 		req.command = WDCC_SMART;
1226 		req.databuf = (caddr_t) inbuf;
1227 		req.datalen = sizeof(inbuf);
1228 		req.cylinder = WDSMART_CYL;
1229 		req.timeout = 1000;
1230 
1231 		ata_command(&req);
1232 
1233 		print_error(inbuf);
1234 	} else if (strcmp(argv[0], "selftest-log") == 0) {
1235 		if (!is_smart()) {
1236 			fprintf(stderr, "SMART not supported\n");
1237 			return;
1238 		}
1239 
1240 		memset(&inbuf, 0, sizeof(inbuf));
1241 		memset(&req, 0, sizeof(req));
1242 
1243 		req.flags = ATACMD_READ;
1244 		req.features = WDSM_RD_LOG;
1245 		req.sec_count = 1;
1246 		req.sec_num = 6;
1247 		req.command = WDCC_SMART;
1248 		req.databuf = (caddr_t) inbuf;
1249 		req.datalen = sizeof(inbuf);
1250 		req.cylinder = WDSMART_CYL;
1251 		req.timeout = 1000;
1252 
1253 		ata_command(&req);
1254 
1255 		print_selftest(inbuf);
1256 
1257 	} else {
1258 		usage();
1259 	}
1260 	return;
1261 }
1262 
1263 void
1264 device_security(int argc, char *argv[])
1265 {
1266 	struct atareq req;
1267 	struct ataparams *inqbuf;
1268 
1269 	/* need subcommand */
1270 	if (argc < 1)
1271 		usage();
1272 
1273 	if (strcmp(argv[0], "freeze") == 0) {
1274 		memset(&req, 0, sizeof(req));
1275 		req.command = WDCC_SECURITY_FREEZE;
1276 		req.timeout = 1000;
1277 		ata_command(&req);
1278 	} else if (strcmp(argv[0], "status") == 0) {
1279 		inqbuf = getataparams();
1280 		print_bitinfo("\t", "\n", inqbuf->atap_sec_st, ata_sec_st);
1281 	} else
1282 		usage();
1283 
1284 	return;
1285 }
1286 
1287 /*
1288  * bus_reset:
1289  *	Reset an ATA bus (will reset all devices on the bus)
1290  */
1291 void
1292 bus_reset(int argc, char *argv[])
1293 {
1294 	int error;
1295 
1296 	/* no args */
1297 	if (argc != 0)
1298 		usage();
1299 
1300 	error = ioctl(fd, ATABUSIORESET, NULL);
1301 
1302 	if (error == -1)
1303 		err(1, "ATABUSIORESET failed");
1304 }
1305