xref: /openbsd-src/sbin/atactl/atactl.c (revision 5095123fcd621e1f1356d8fda42ce7aeb7e57f5b)
1*5095123fSyasuoka /*	$OpenBSD: atactl.c,v 1.49 2023/04/30 00:58:38 yasuoka Exp $	*/
2239cdf63Scsapuntz /*	$NetBSD: atactl.c,v 1.4 1999/02/24 18:49:14 jwise Exp $	*/
3239cdf63Scsapuntz 
4239cdf63Scsapuntz /*-
5239cdf63Scsapuntz  * Copyright (c) 1998 The NetBSD Foundation, Inc.
6239cdf63Scsapuntz  * All rights reserved.
7239cdf63Scsapuntz  *
8239cdf63Scsapuntz  * This code is derived from software contributed to The NetBSD Foundation
9239cdf63Scsapuntz  * by Ken Hornstein.
10239cdf63Scsapuntz  *
11239cdf63Scsapuntz  * Redistribution and use in source and binary forms, with or without
12239cdf63Scsapuntz  * modification, are permitted provided that the following conditions
13239cdf63Scsapuntz  * are met:
14239cdf63Scsapuntz  * 1. Redistributions of source code must retain the above copyright
15239cdf63Scsapuntz  *    notice, this list of conditions and the following disclaimer.
16239cdf63Scsapuntz  * 2. Redistributions in binary form must reproduce the above copyright
17239cdf63Scsapuntz  *    notice, this list of conditions and the following disclaimer in the
18239cdf63Scsapuntz  *    documentation and/or other materials provided with the distribution.
19239cdf63Scsapuntz  *
20239cdf63Scsapuntz  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21239cdf63Scsapuntz  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22239cdf63Scsapuntz  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23239cdf63Scsapuntz  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24239cdf63Scsapuntz  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25239cdf63Scsapuntz  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26239cdf63Scsapuntz  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27239cdf63Scsapuntz  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28239cdf63Scsapuntz  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29239cdf63Scsapuntz  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30239cdf63Scsapuntz  * POSSIBILITY OF SUCH DAMAGE.
31239cdf63Scsapuntz  */
32239cdf63Scsapuntz 
33239cdf63Scsapuntz /*
34239cdf63Scsapuntz  * atactl(8) - a program to control ATA devices.
35239cdf63Scsapuntz  */
36239cdf63Scsapuntz 
37b9fc9a72Sderaadt #include <sys/param.h>	/* DEV_BSIZE */
38239cdf63Scsapuntz #include <sys/ioctl.h>
3930520cf8Sgluk 
40239cdf63Scsapuntz #include <err.h>
41239cdf63Scsapuntz #include <errno.h>
42239cdf63Scsapuntz #include <fcntl.h>
43239cdf63Scsapuntz #include <stdio.h>
44239cdf63Scsapuntz #include <stdlib.h>
45239cdf63Scsapuntz #include <string.h>
46239cdf63Scsapuntz #include <unistd.h>
47239cdf63Scsapuntz #include <util.h>
48239cdf63Scsapuntz 
49239cdf63Scsapuntz #include <dev/ata/atareg.h>
50239cdf63Scsapuntz #include <dev/ic/wdcreg.h>
51b8b1ca5bSgrange #include <dev/ic/wdcevent.h>
52239cdf63Scsapuntz #include <sys/ataio.h>
53239cdf63Scsapuntz 
546e60c95aSgluk #include "atasec.h"
5530520cf8Sgluk #include "atasmart.h"
5630520cf8Sgluk 
57239cdf63Scsapuntz struct command {
58239cdf63Scsapuntz 	const char *cmd_name;
59c72b5b24Smillert 	void (*cmd_func)(int, char *[]);
60239cdf63Scsapuntz };
61239cdf63Scsapuntz 
62239cdf63Scsapuntz struct bitinfo {
63239cdf63Scsapuntz 	u_int bitmask;
64239cdf63Scsapuntz 	const char *string;
65239cdf63Scsapuntz };
66239cdf63Scsapuntz 
6730520cf8Sgluk struct valinfo {
6830520cf8Sgluk 	int value;
6930520cf8Sgluk 	const char *string;
7030520cf8Sgluk };
7130520cf8Sgluk 
72c72b5b24Smillert int  main(int, char *[]);
73dc223e5cSgrange __dead void usage(void);
74c72b5b24Smillert void ata_command(struct atareq *);
75c72b5b24Smillert void print_bitinfo(const char *, u_int, struct bitinfo *);
7630520cf8Sgluk int  strtoval(const char *, struct valinfo *);
77be0cee2fSsemarie const char *valtostr(int, struct valinfo *, const char *);
78239cdf63Scsapuntz 
79239cdf63Scsapuntz int	fd;				/* file descriptor for device */
80239cdf63Scsapuntz 
814bf7812dSmoritz extern char *__progname;		/* from crt0.o */
82239cdf63Scsapuntz 
8352ffca01Scsapuntz void    device_dump(int, char*[]);
84c72b5b24Smillert void	device_identify(int, char *[]);
85c72b5b24Smillert void	device_setidle(int, char *[]);
86c72b5b24Smillert void	device_idle(int, char *[]);
87c72b5b24Smillert void	device_checkpower(int, char *[]);
88c72b5b24Smillert void	device_acoustic(int, char *[]);
89c72b5b24Smillert void	device_apm(int, char *[]);
90c72b5b24Smillert void	device_feature(int, char *[]);
916e60c95aSgluk void	device_sec_setpass(int, char *[]);
926e60c95aSgluk void	device_sec_unlock(int, char *[]);
936e60c95aSgluk void	device_sec_erase(int, char *[]);
946e60c95aSgluk void	device_sec_freeze(int, char *[]);
956e60c95aSgluk void	device_sec_disablepass(int, char *[]);
9630520cf8Sgluk void	device_smart_enable(int, char *[]);
9730520cf8Sgluk void	device_smart_disable(int, char *[]);
9830520cf8Sgluk void	device_smart_status(int, char *[]);
9930520cf8Sgluk void	device_smart_autosave(int, char *[]);
10030520cf8Sgluk void	device_smart_offline(int, char *[]);
10130520cf8Sgluk void	device_smart_read(int, char *[]);
10230520cf8Sgluk void	device_smart_readlog(int, char *[]);
10330520cf8Sgluk void	device_attr(int, char *[]);
10430520cf8Sgluk 
10530520cf8Sgluk void	smart_print_errdata(struct smart_log_errdata *);
1064bf7812dSmoritz int	smart_cksum(u_int8_t *, size_t);
107239cdf63Scsapuntz 
1086e60c95aSgluk char 	*sec_getpass(int, int);
1096e60c95aSgluk 
110239cdf63Scsapuntz struct command commands[] = {
11152ffca01Scsapuntz 	{ "dump",		device_dump },
112239cdf63Scsapuntz 	{ "identify",		device_identify },
113239cdf63Scsapuntz 	{ "setidle",		device_setidle },
114239cdf63Scsapuntz 	{ "setstandby",		device_setidle },
115239cdf63Scsapuntz 	{ "idle",		device_idle },
116239cdf63Scsapuntz 	{ "standby",		device_idle },
117239cdf63Scsapuntz 	{ "sleep",		device_idle },
118239cdf63Scsapuntz 	{ "checkpower",		device_checkpower },
1193ded539eSderaadt 	{ "acousticdisable",	device_feature },
1203ded539eSderaadt 	{ "acousticset",	device_acoustic },
1213ded539eSderaadt 	{ "apmdisable",		device_feature },
1223ded539eSderaadt 	{ "apmset",		device_apm },
1233ded539eSderaadt 	{ "poddisable",		device_feature },
1243ded539eSderaadt 	{ "podenable",		device_feature },
1253ded539eSderaadt 	{ "puisdisable",	device_feature },
1263ded539eSderaadt 	{ "puisenable",		device_feature },
1273ded539eSderaadt 	{ "puisspinup",		device_feature },
1283ded539eSderaadt 	{ "readaheaddisable",	device_feature },
1293ded539eSderaadt 	{ "readaheadenable",	device_feature },
1306e60c95aSgluk 	{ "secsetpass",		device_sec_setpass },
1316e60c95aSgluk 	{ "secunlock",		device_sec_unlock },
1326e60c95aSgluk 	{ "secerase",		device_sec_erase },
1336e60c95aSgluk 	{ "secfreeze",		device_sec_freeze },
1346e60c95aSgluk 	{ "secdisablepass",	device_sec_disablepass },
13530520cf8Sgluk 	{ "smartenable", 	device_smart_enable },
13630520cf8Sgluk 	{ "smartdisable", 	device_smart_disable },
13730520cf8Sgluk 	{ "smartstatus", 	device_smart_status },
13830520cf8Sgluk 	{ "smartautosave",	device_smart_autosave },
13930520cf8Sgluk 	{ "smartoffline",	device_smart_offline },
14030520cf8Sgluk 	{ "smartread",		device_smart_read },
14130520cf8Sgluk 	{ "smartreadlog",	device_smart_readlog },
14230520cf8Sgluk 	{ "readattr",		device_attr },
1433ded539eSderaadt 	{ "writecachedisable",	device_feature },
1443ded539eSderaadt 	{ "writecacheenable",	device_feature },
145239cdf63Scsapuntz 	{ NULL,		NULL },
146239cdf63Scsapuntz };
147239cdf63Scsapuntz 
148239cdf63Scsapuntz /*
149239cdf63Scsapuntz  * Tables containing bitmasks used for error reporting and
150239cdf63Scsapuntz  * device identification.
151239cdf63Scsapuntz  */
152239cdf63Scsapuntz 
153239cdf63Scsapuntz struct bitinfo ata_caps[] = {
154239cdf63Scsapuntz 	{ ATA_CAP_STBY, "ATA standby timer values" },
155239cdf63Scsapuntz 	{ WDC_CAP_IORDY, "IORDY operation" },
156239cdf63Scsapuntz 	{ WDC_CAP_IORDY_DSBL, "IORDY disabling" },
157691235adSmiod 	{ 0, NULL },
158239cdf63Scsapuntz };
159239cdf63Scsapuntz 
160239cdf63Scsapuntz struct bitinfo ata_vers[] = {
161239cdf63Scsapuntz 	{ WDC_VER_ATA1,	 "ATA-1" },
162239cdf63Scsapuntz 	{ WDC_VER_ATA2,	 "ATA-2" },
163239cdf63Scsapuntz 	{ WDC_VER_ATA3,	 "ATA-3" },
164239cdf63Scsapuntz 	{ WDC_VER_ATA4,	 "ATA-4" },
1653ded539eSderaadt 	{ WDC_VER_ATA5,	 "ATA-5" },
1663ded539eSderaadt 	{ WDC_VER_ATA6,	 "ATA-6" },
1673ded539eSderaadt 	{ WDC_VER_ATA7,	 "ATA-7" },
1683ded539eSderaadt 	{ WDC_VER_ATA8,	 "ATA-8" },
1693ded539eSderaadt 	{ WDC_VER_ATA9,	 "ATA-9" },
1703ded539eSderaadt 	{ WDC_VER_ATA10, "ATA-10" },
1713ded539eSderaadt 	{ WDC_VER_ATA11, "ATA-11" },
1723ded539eSderaadt 	{ WDC_VER_ATA12, "ATA-12" },
1733ded539eSderaadt 	{ WDC_VER_ATA13, "ATA-13" },
1743ded539eSderaadt 	{ WDC_VER_ATA14, "ATA-14" },
175691235adSmiod 	{ 0, NULL },
176239cdf63Scsapuntz };
177239cdf63Scsapuntz 
178239cdf63Scsapuntz struct bitinfo ata_cmd_set1[] = {
179239cdf63Scsapuntz 	{ WDC_CMD1_NOP, "NOP command" },
180239cdf63Scsapuntz 	{ WDC_CMD1_RB, "READ BUFFER command" },
181239cdf63Scsapuntz 	{ WDC_CMD1_WB, "WRITE BUFFER command" },
182239cdf63Scsapuntz 	{ WDC_CMD1_HPA, "Host Protected Area feature set" },
183239cdf63Scsapuntz 	{ WDC_CMD1_DVRST, "DEVICE RESET command" },
184239cdf63Scsapuntz 	{ WDC_CMD1_SRV, "SERVICE interrupt" },
185b6d9a37cSavsm 	{ WDC_CMD1_RLSE, "Release interrupt" },
186b6d9a37cSavsm 	{ WDC_CMD1_AHEAD, "Read look-ahead" },
187b6d9a37cSavsm 	{ WDC_CMD1_CACHE, "Write cache" },
188239cdf63Scsapuntz 	{ WDC_CMD1_PKT, "PACKET command feature set" },
189239cdf63Scsapuntz 	{ WDC_CMD1_PM, "Power Management feature set" },
190239cdf63Scsapuntz 	{ WDC_CMD1_REMOV, "Removable Media feature set" },
191239cdf63Scsapuntz 	{ WDC_CMD1_SEC, "Security Mode feature set" },
192239cdf63Scsapuntz 	{ WDC_CMD1_SMART, "SMART feature set" },
193691235adSmiod 	{ 0, NULL },
194239cdf63Scsapuntz };
195239cdf63Scsapuntz 
196239cdf63Scsapuntz struct bitinfo ata_cmd_set2[] = {
1973ded539eSderaadt 	{ ATAPI_CMD2_FCE, "Flush Cache Ext command" },
1983ded539eSderaadt 	{ ATAPI_CMD2_FC, "Flush Cache command" },
1993ded539eSderaadt 	{ ATAPI_CMD2_DCO, "Device Configuration Overlay feature set" },
2003ded539eSderaadt 	{ ATAPI_CMD2_48AD, "48bit address feature set" },
2013ded539eSderaadt 	{ ATAPI_CMD2_AAM, "Automatic Acoustic Management feature set" },
2023ded539eSderaadt 	{ ATAPI_CMD2_SM, "Set Max security extension commands" },
2033ded539eSderaadt 	{ ATAPI_CMD2_SF, "Set Features subcommand required" },
2043ded539eSderaadt 	{ ATAPI_CMD2_PUIS, "Power-up in standby feature set" },
205239cdf63Scsapuntz 	{ WDC_CMD2_RMSN, "Removable Media Status Notification feature set" },
206239cdf63Scsapuntz 	{ ATA_CMD2_APM, "Advanced Power Management feature set" },
207239cdf63Scsapuntz 	{ ATA_CMD2_CFA, "CFA feature set" },
208941242ddSchris 	{ ATA_CMD2_RWQ, "READ/WRITE DMA QUEUED commands" },
209239cdf63Scsapuntz 	{ WDC_CMD2_DM, "DOWNLOAD MICROCODE command" },
210691235adSmiod 	{ 0, NULL },
211239cdf63Scsapuntz };
212239cdf63Scsapuntz 
2133ded539eSderaadt struct bitinfo ata_cmd_ext[] = {
214abceec7cSjsg 	{ ATAPI_CMDE_IIUF, "IDLE IMMEDIATE with UNLOAD FEATURE" },
2153ded539eSderaadt 	{ ATAPI_CMDE_MSER, "Media serial number" },
2163ded539eSderaadt 	{ ATAPI_CMDE_TEST, "SMART self-test" },
2173ded539eSderaadt 	{ ATAPI_CMDE_SLOG, "SMART error logging" },
218691235adSmiod 	{ 0, NULL },
2193ded539eSderaadt };
2203ded539eSderaadt 
22130520cf8Sgluk /*
22230520cf8Sgluk  * Tables containing bitmasks and values used for
22330520cf8Sgluk  * SMART commands.
22430520cf8Sgluk  */
22530520cf8Sgluk 
22630520cf8Sgluk struct bitinfo smart_offcap[] = {
22730520cf8Sgluk 	{ SMART_OFFCAP_EXEC, "execute immediate" },
22830520cf8Sgluk 	{ SMART_OFFCAP_ABORT, "abort/restart" },
22930520cf8Sgluk 	{ SMART_OFFCAP_READSCAN, "read scanning" },
23030520cf8Sgluk 	{ SMART_OFFCAP_SELFTEST, "self-test routines" },
23130520cf8Sgluk 	{ 0, NULL}
23230520cf8Sgluk };
23330520cf8Sgluk 
23430520cf8Sgluk struct bitinfo smart_smartcap[] = {
23530520cf8Sgluk 	{ SMART_SMARTCAP_SAVE, "saving SMART data" },
23630520cf8Sgluk 	{ SMART_SMARTCAP_AUTOSAVE, "enable/disable attribute autosave" },
23730520cf8Sgluk 	{ 0, NULL }
23830520cf8Sgluk };
23930520cf8Sgluk 
24030520cf8Sgluk struct valinfo smart_autosave[] = {
24130520cf8Sgluk 	{ SMART_AUTOSAVE_EN, "enable" },
24230520cf8Sgluk 	{ SMART_AUTOSAVE_DS, "disable" },
24330520cf8Sgluk 	{ 0, NULL }
24430520cf8Sgluk };
24530520cf8Sgluk 
24630520cf8Sgluk struct valinfo smart_offline[] = {
24730520cf8Sgluk 	{ SMART_OFFLINE_COLLECT, "collect" },
24830520cf8Sgluk 	{ SMART_OFFLINE_SHORTOFF, "shortoffline" },
24930520cf8Sgluk 	{ SMART_OFFLINE_EXTENOFF, "extenoffline" },
25030520cf8Sgluk 	{ SMART_OFFLINE_ABORT, "abort" },
25130520cf8Sgluk 	{ SMART_OFFLINE_SHORTCAP, "shortcaptive" },
25230520cf8Sgluk 	{ SMART_OFFLINE_EXTENCAP, "extencaptive" },
25330520cf8Sgluk 	{ 0, NULL }
25430520cf8Sgluk };
25530520cf8Sgluk 
25630520cf8Sgluk struct valinfo smart_readlog[] = {
25730520cf8Sgluk 	{ SMART_READLOG_DIR, "directory" },
25830520cf8Sgluk 	{ SMART_READLOG_SUM, "summary" },
25930520cf8Sgluk 	{ SMART_READLOG_COMP, "comp" },
26030520cf8Sgluk 	{ SMART_READLOG_SELF, "selftest" },
26130520cf8Sgluk 	{ 0, NULL }
26230520cf8Sgluk };
26330520cf8Sgluk 
26430520cf8Sgluk struct valinfo smart_offstat[] = {
26530520cf8Sgluk 	{ SMART_OFFSTAT_NOTSTART, "never started" },
26630520cf8Sgluk 	{ SMART_OFFSTAT_COMPLETE, "completed ok" },
26730520cf8Sgluk 	{ SMART_OFFSTAT_SUSPEND, "suspended by an interrupting command" },
26830520cf8Sgluk 	{ SMART_OFFSTAT_INTR, "aborted by an interrupting command" },
26930520cf8Sgluk 	{ SMART_OFFSTAT_ERROR, "aborted due to fatal error" },
27030520cf8Sgluk 	{ 0, NULL }
27130520cf8Sgluk };
27230520cf8Sgluk 
27330520cf8Sgluk struct valinfo smart_selfstat[] = {
27430520cf8Sgluk 	{ SMART_SELFSTAT_COMPLETE, "completed ok or not started" },
27530520cf8Sgluk 	{ SMART_SELFSTAT_ABORT, "aborted" },
27630520cf8Sgluk 	{ SMART_SELFSTAT_INTR, "hardware or software reset" },
27730520cf8Sgluk 	{ SMART_SELFSTAT_ERROR, "fatal error" },
27830520cf8Sgluk 	{ SMART_SELFSTAT_UNKFAIL, "unknown test element failed" },
27930520cf8Sgluk 	{ SMART_SELFSTAT_ELFAIL, "electrical test element failed" },
28030520cf8Sgluk 	{ SMART_SELFSTAT_SRVFAIL, "servo test element failed" },
28130520cf8Sgluk 	{ SMART_SELFSTAT_RDFAIL, "read test element failed" },
28230520cf8Sgluk 	{ 0, NULL }
28330520cf8Sgluk };
28430520cf8Sgluk 
28530520cf8Sgluk struct valinfo smart_logstat[] = {
28630520cf8Sgluk 	{ SMART_LOG_STATE_UNK, "unknown" },
28730520cf8Sgluk 	{ SMART_LOG_STATE_SLEEP, "sleep" },
28830520cf8Sgluk 	{ SMART_LOG_STATE_ACTIDL, "active/idle" },
28930520cf8Sgluk 	{ SMART_LOG_STATE_OFFSELF, "off-line or self-test" },
29030520cf8Sgluk 	{ 0, NULL }
29130520cf8Sgluk };
29230520cf8Sgluk 
29330520cf8Sgluk /*
29430520cf8Sgluk  * Tables containing values used for reading
29530520cf8Sgluk  * device attributes.
29630520cf8Sgluk  */
29730520cf8Sgluk 
29830520cf8Sgluk struct valinfo ibm_attr_names[] = {
29930520cf8Sgluk 	{ 1, "Raw Read Error Rate" },
30030520cf8Sgluk 	{ 2, "Throughput Performance" },
30130520cf8Sgluk 	{ 3, "Spin Up Time" },
30230520cf8Sgluk 	{ 4, "Start/Stop Count" },
30330520cf8Sgluk 	{ 5, "Reallocated Sector Count" },
3043de3da92Sgrange 	{ 6, "Read Channel Margin" },
30530520cf8Sgluk 	{ 7, "Seek Error Rate" },
30630520cf8Sgluk 	{ 8, "Seek Time Performance" },
3073de3da92Sgrange 	{ 9, "Power-On Hours Count" },
30830520cf8Sgluk 	{ 10, "Spin Retry Count" },
3093de3da92Sgrange 	{ 11, "Calibration Retry Count" },
31030520cf8Sgluk 	{ 12, "Device Power Cycle Count" },
3113de3da92Sgrange 	{ 13, "Soft Read Error Rate" },
312f16549d6Smiod 	{ 100, "Erase/Program Cycles" },
313f16549d6Smiod 	{ 103, "Translation Table Rebuild" },
314f16549d6Smiod 	{ 160, "Uncorrectable Error Count" },
315f16549d6Smiod 	{ 170, "Reserved Block Count" },
316f16549d6Smiod 	{ 171, "Program Fail Count" },
317f16549d6Smiod 	{ 172, "Erase Fail Count" },
318f16549d6Smiod 	{ 173, "Wear Worst Case Erase Count" },
319f16549d6Smiod 	{ 174, "Power-Off Retract Count" },
320f16549d6Smiod 	{ 175, "Program Fail Count" },
321f16549d6Smiod 	{ 176, "Erase Fail Count" },
322f16549d6Smiod 	{ 177, "Wear Leveling Count" },
323f16549d6Smiod 	{ 178, "Used Reserved Block Count" },
324f16549d6Smiod 	{ 179, "Used Reserved Block Count Total" },
325f16549d6Smiod 	{ 180, "Unused Reserved Block Count Total" },
326f16549d6Smiod 	{ 181, "Program Fail Count Total" },
327f16549d6Smiod 	{ 182, "Erase Fail Count" },
328f16549d6Smiod 	{ 183, "Runtime Bad Block" },
329f16549d6Smiod 	{ 184, "End-to-End error" },
330f16549d6Smiod 	{ 185, "Head Stability" },
331f16549d6Smiod 	{ 186, "Induced Op-Vibration Detection" },
332f16549d6Smiod 	{ 187, "Reported Uncorrectable Errors" },
333f16549d6Smiod 	{ 188, "Command Timeout" },
3343de3da92Sgrange 	{ 189, "High Fly Writes" },
3353de3da92Sgrange 	{ 190, "Airflow Temperature" },
3363de3da92Sgrange 	{ 191, "G-Sense Error Rate" },
3373de3da92Sgrange 	{ 192, "Power-Off Retract Count" },
33830520cf8Sgluk 	{ 193, "Load Cycle Count" },
33930520cf8Sgluk 	{ 194, "Temperature" },
3403de3da92Sgrange 	{ 195, "Hardware ECC Recovered" },
34130520cf8Sgluk 	{ 196, "Reallocation Event Count" },
34230520cf8Sgluk 	{ 197, "Current Pending Sector Count" },
3433de3da92Sgrange 	{ 198, "Off-Line Scan Uncorrectable Sector Count" },
34430520cf8Sgluk 	{ 199, "Ultra DMA CRC Error Count" },
3453de3da92Sgrange 	{ 200, "Write Error Rate" },
3463de3da92Sgrange 	{ 201, "Soft Read Error Rate" },
3473de3da92Sgrange 	{ 202, "Data Address Mark Errors" },
3483de3da92Sgrange 	{ 203, "Run Out Cancel" },
3493de3da92Sgrange 	{ 204, "Soft ECC Correction" },
3503de3da92Sgrange 	{ 205, "Thermal Asperity Check" },
3513de3da92Sgrange 	{ 206, "Flying Height" },
3523de3da92Sgrange 	{ 207, "Spin High Current" },
3533de3da92Sgrange 	{ 208, "Spin Buzz" },
3543de3da92Sgrange 	{ 209, "Offline Seek Performance" },
3553de3da92Sgrange 	{ 220, "Disk Shift" },
3563de3da92Sgrange 	{ 221, "G-Sense Error Rate" },
3573de3da92Sgrange 	{ 222, "Loaded Hours" },
3583de3da92Sgrange 	{ 223, "Load/Unload Retry Count" },
3593de3da92Sgrange 	{ 224, "Load Friction" },
3603de3da92Sgrange 	{ 225, "Load/Unload Cycle Count" },
3613de3da92Sgrange 	{ 226, "Load-In Time" },
3623de3da92Sgrange 	{ 227, "Torque Amplification Count" },
3633de3da92Sgrange 	{ 228, "Power-Off Retract Count" },
3643de3da92Sgrange 	{ 230, "GMR Head Amplitude" },
3653de3da92Sgrange 	{ 231, "Temperature" },
366f16549d6Smiod 	{ 232, "Available reserved space" },
367f16549d6Smiod 	{ 233, "Media wearout indicator" },
368f16549d6Smiod 	{ 235, "Power-Off Retract Count" },
3693de3da92Sgrange 	{ 240, "Head Flying Hours" },
370f16549d6Smiod 	{ 241, "Total LBAs Written" },
371f16549d6Smiod 	{ 242, "Total LBAs Read" },
372f16549d6Smiod 	{ 249, "NAND Writes (1GB)" },
3733de3da92Sgrange 	{ 250, "Read Error Retry Rate" },
374f16549d6Smiod 	{ 254, "Free Fall Sensor" },
37530520cf8Sgluk 	{ 0, NULL },
37630520cf8Sgluk };
37730520cf8Sgluk 
37830520cf8Sgluk #define MAKEWORD(b1, b2) \
37930520cf8Sgluk 	(b2 << 8 | b1)
38030520cf8Sgluk #define MAKEDWORD(b1, b2, b3, b4) \
38130520cf8Sgluk 	(b4 << 24 | b3 << 16 | b2 << 8 | b1)
38230520cf8Sgluk 
383239cdf63Scsapuntz int
main(int argc,char * argv[])384bc52e260Sderaadt main(int argc, char *argv[])
385239cdf63Scsapuntz {
38630520cf8Sgluk 	struct command	*cmdp;
387239cdf63Scsapuntz 
38830520cf8Sgluk 	if (argc < 2)
389d790d512Sderaadt 		usage();
390239cdf63Scsapuntz 
391239cdf63Scsapuntz 	/*
392239cdf63Scsapuntz 	 * Open the device
393239cdf63Scsapuntz 	 */
39485aa0849Soga 	if ((fd = opendev(argv[1], O_RDWR, OPENDEV_PART, NULL)) == -1)
39530520cf8Sgluk 		err(1, "%s", argv[1]);
396239cdf63Scsapuntz 
39730520cf8Sgluk 	/* Skip program name and device name. */
39830520cf8Sgluk 	if (argc != 2) {
39930520cf8Sgluk 		argv += 2;
40030520cf8Sgluk 		argc -= 2;
40130520cf8Sgluk 	} else {
40230520cf8Sgluk 		argv[1] = "identify";
40330520cf8Sgluk 		argv += 1;
40430520cf8Sgluk 		argc -= 1;
40530520cf8Sgluk 	}
406239cdf63Scsapuntz 
407239cdf63Scsapuntz 	/* Look up and call the command. */
40830520cf8Sgluk 	for (cmdp = commands; cmdp->cmd_name != NULL; cmdp++)
40930520cf8Sgluk 		if (strcmp(argv[0], cmdp->cmd_name) == 0)
410239cdf63Scsapuntz 			break;
41130520cf8Sgluk 	if (cmdp->cmd_name == NULL)
41230520cf8Sgluk 		errx(1, "unknown command: %s", argv[0]);
413239cdf63Scsapuntz 
41430520cf8Sgluk 	(cmdp->cmd_func)(argc, argv);
41566420897Smickey 
41666420897Smickey 	return (0);
417239cdf63Scsapuntz }
418239cdf63Scsapuntz 
419dc223e5cSgrange __dead void
usage(void)4208809fabbSderaadt usage(void)
421239cdf63Scsapuntz {
422239cdf63Scsapuntz 
423d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device [command [arg]]\n", __progname);
424239cdf63Scsapuntz 	exit(1);
425239cdf63Scsapuntz }
426239cdf63Scsapuntz 
427239cdf63Scsapuntz /*
428239cdf63Scsapuntz  * Wrapper that calls ATAIOCCOMMAND and checks for errors
429239cdf63Scsapuntz  */
430239cdf63Scsapuntz void
ata_command(struct atareq * req)431bc52e260Sderaadt ata_command(struct atareq *req)
432239cdf63Scsapuntz {
43330638706Sderaadt 	if (ioctl(fd, ATAIOCCOMMAND, req) == -1)
434239cdf63Scsapuntz 		err(1, "ATAIOCCOMMAND failed");
435239cdf63Scsapuntz 
436239cdf63Scsapuntz 	switch (req->retsts) {
437239cdf63Scsapuntz 
438239cdf63Scsapuntz 	case ATACMD_OK:
439239cdf63Scsapuntz 		return;
440239cdf63Scsapuntz 	case ATACMD_TIMEOUT:
441dc223e5cSgrange 		errx(1, "ATA command timed out");
442239cdf63Scsapuntz 	case ATACMD_DF:
443dc223e5cSgrange 		errx(1, "ATA device returned a Device Fault");
444239cdf63Scsapuntz 	case ATACMD_ERROR:
445239cdf63Scsapuntz 		if (req->error & WDCE_ABRT)
446dc223e5cSgrange 			errx(1, "ATA device returned Aborted Command");
447239cdf63Scsapuntz 		else
448dc223e5cSgrange 			errx(1, "ATA device returned error register %0x",
449dc223e5cSgrange 			    req->error);
450239cdf63Scsapuntz 	default:
451dc223e5cSgrange 		errx(1, "ATAIOCCOMMAND returned unknown result code %d",
452dc223e5cSgrange 		    req->retsts);
453239cdf63Scsapuntz 	}
454239cdf63Scsapuntz }
455239cdf63Scsapuntz 
456239cdf63Scsapuntz /*
457239cdf63Scsapuntz  * Print out strings associated with particular bitmasks
458239cdf63Scsapuntz  */
459239cdf63Scsapuntz void
print_bitinfo(const char * f,u_int bits,struct bitinfo * binfo)460bc52e260Sderaadt print_bitinfo(const char *f, u_int bits, struct bitinfo *binfo)
461239cdf63Scsapuntz {
462239cdf63Scsapuntz 
463691235adSmiod 	for (; binfo->bitmask != 0; binfo++)
464239cdf63Scsapuntz 		if (bits & binfo->bitmask)
46549c85f06Sderaadt 			printf(f, binfo->string);
466239cdf63Scsapuntz }
467239cdf63Scsapuntz 
468239cdf63Scsapuntz /*
46930520cf8Sgluk  * strtoval():
47030520cf8Sgluk  *    returns value associated with given string,
47130520cf8Sgluk  *    if no value found -1 is returned.
47230520cf8Sgluk  */
47330520cf8Sgluk int
strtoval(const char * str,struct valinfo * vinfo)474bc52e260Sderaadt strtoval(const char *str, struct valinfo *vinfo)
47530520cf8Sgluk {
47630520cf8Sgluk 	for (; vinfo->string != NULL; vinfo++)
47730520cf8Sgluk 		if (strcmp(str, vinfo->string) == 0)
478dc223e5cSgrange 			return (vinfo->value);
479dc223e5cSgrange 	return (-1);
48030520cf8Sgluk }
48130520cf8Sgluk 
48230520cf8Sgluk /*
48330520cf8Sgluk  * valtostr():
48430520cf8Sgluk  *    returns string associated with given value,
485be0cee2fSsemarie  *    if no string found def value is returned.
48630520cf8Sgluk  */
48730520cf8Sgluk const char *
valtostr(int val,struct valinfo * vinfo,const char * def)488be0cee2fSsemarie valtostr(int val, struct valinfo *vinfo, const char *def)
48930520cf8Sgluk {
49030520cf8Sgluk 	for (; vinfo->string != NULL; vinfo++)
49130520cf8Sgluk 		if (val == vinfo->value)
492dc223e5cSgrange 			return (vinfo->string);
493be0cee2fSsemarie 	return (def);
49430520cf8Sgluk }
49530520cf8Sgluk 
49630520cf8Sgluk /*
497239cdf63Scsapuntz  * DEVICE COMMANDS
498239cdf63Scsapuntz  */
499dc223e5cSgrange 
500dc223e5cSgrange /*
501dc223e5cSgrange  * device dump:
502dc223e5cSgrange  *
503dc223e5cSgrange  * extract issued ATA requests from the log buffer
504dc223e5cSgrange  */
50552ffca01Scsapuntz void
device_dump(int argc,char * argv[])506bc52e260Sderaadt device_dump(int argc, char *argv[])
50752ffca01Scsapuntz {
50852ffca01Scsapuntz 	unsigned char buf[131072];
509dc223e5cSgrange 	atagettrace_t agt;
5104bf7812dSmoritz 	unsigned int total;
5114bf7812dSmoritz 	unsigned int p = 0;
512b8b1ca5bSgrange 	int type;
513b8b1ca5bSgrange 	const char *types[] = { NULL, "status", "error", "ATAPI",
514b8b1ca5bSgrange 	    "ATAPI done", "ATA cmd", "ATA", "select slave",
515b8b1ca5bSgrange 	    "select master", "register read", "ATA LBA48" };
516b8b1ca5bSgrange 	int num_types = sizeof(types) / sizeof(types[0]);
517b8b1ca5bSgrange 	int info;
518b8b1ca5bSgrange 	int entrysize;
519b8b1ca5bSgrange 	int i;
520b8b1ca5bSgrange 	int flags;
52152ffca01Scsapuntz 
522dc223e5cSgrange 	if (argc != 1)
523dc223e5cSgrange 		goto usage;
524dc223e5cSgrange 
525dc223e5cSgrange 	memset(&agt, 0, sizeof(agt));
526dc223e5cSgrange 	agt.buf_size = sizeof(buf);
527dc223e5cSgrange 	agt.buf = buf;
528dc223e5cSgrange 
52930638706Sderaadt 	if (ioctl(fd, ATAIOGETTRACE, &agt) == -1)
53052ffca01Scsapuntz 		err(1, "ATAIOGETTRACE failed");
53152ffca01Scsapuntz 
532b8b1ca5bSgrange 	total = agt.bytes_copied;
533b8b1ca5bSgrange 
534b8b1ca5bSgrange 	/* Parse entries */
535b8b1ca5bSgrange 	while (p < total) {
536b8b1ca5bSgrange 		type = buf[p++];
537b8b1ca5bSgrange 		if (p >= total)
538b8b1ca5bSgrange 			return;
539b8b1ca5bSgrange 		if (type <= 0 || type >= num_types)
540b8b1ca5bSgrange 			return;
541b8b1ca5bSgrange 
542b8b1ca5bSgrange 		info = buf[p++];
543b8b1ca5bSgrange 		if (p >= total)
544b8b1ca5bSgrange 			return;
545b8b1ca5bSgrange 		entrysize = (info & 0x1f);
546b8b1ca5bSgrange 
547b8b1ca5bSgrange 		printf ("ch %d", (info >> 5) & 0x7);
548b8b1ca5bSgrange 		printf(": %s", types[type]);
549b8b1ca5bSgrange 
550b8b1ca5bSgrange 		switch (type) {
551b8b1ca5bSgrange 		case WDCEVENT_STATUS:
552b8b1ca5bSgrange 			if (entrysize != 1)
553b8b1ca5bSgrange 				return;
554b8b1ca5bSgrange 
555b8b1ca5bSgrange 			printf(": 0x%x", buf[p]);
556b8b1ca5bSgrange 			if (buf[p] & WDCS_BSY)
557b8b1ca5bSgrange 				printf(" BSY");
558b8b1ca5bSgrange 			if (buf[p] & WDCS_DRDY)
559b8b1ca5bSgrange 				printf(" DRDY");
560b8b1ca5bSgrange 			if (buf[p] & WDCS_DWF)
561b8b1ca5bSgrange 				printf(" DWF");
562b8b1ca5bSgrange 			if (buf[p] & WDCS_DSC)
563b8b1ca5bSgrange 				printf(" DSC");
564b8b1ca5bSgrange 			if (buf[p] & WDCS_DRQ)
565b8b1ca5bSgrange 				printf(" DRQ");
566b8b1ca5bSgrange 			if (buf[p] & WDCS_CORR)
567b8b1ca5bSgrange 				printf(" CORR");
568b8b1ca5bSgrange 			if (buf[p] & WDCS_IDX)
569b8b1ca5bSgrange 				printf(" IDX");
570b8b1ca5bSgrange 			if (buf[p] & WDCS_ERR)
571b8b1ca5bSgrange 				printf(" ERR");
572b8b1ca5bSgrange 
573b8b1ca5bSgrange 			p++;
574b8b1ca5bSgrange 			entrysize = 0;
575b8b1ca5bSgrange 			break;
576b8b1ca5bSgrange 		case WDCEVENT_ERROR:
577b8b1ca5bSgrange 			if (entrysize != 1)
578b8b1ca5bSgrange 				return;
579b8b1ca5bSgrange 
580b8b1ca5bSgrange 			printf(": 0x%x", buf[p]);
581b8b1ca5bSgrange 			if (buf[p] & WDCE_BBK)
582b8b1ca5bSgrange 				printf(" BBK/CRC");
583b8b1ca5bSgrange 			if (buf[p] & WDCE_UNC)
584b8b1ca5bSgrange 				printf(" UNC");
585b8b1ca5bSgrange 			if (buf[p] & WDCE_MC)
586b8b1ca5bSgrange 				printf(" MC");
587b8b1ca5bSgrange 			if (buf[p] & WDCE_IDNF)
588b8b1ca5bSgrange 				printf(" IDNF");
589b8b1ca5bSgrange 			if (buf[p] & WDCE_MCR)
590b8b1ca5bSgrange 				printf(" MCR");
591b8b1ca5bSgrange 			if (buf[p] & WDCE_ABRT)
592b8b1ca5bSgrange 				printf(" ABRT");
593b8b1ca5bSgrange 			if (buf[p] & WDCE_TK0NF)
594b8b1ca5bSgrange 				printf(" TK0NF");
595b8b1ca5bSgrange 			if (buf[p] & WDCE_AMNF)
596b8b1ca5bSgrange 				printf(" AMNF");
597b8b1ca5bSgrange 
598b8b1ca5bSgrange 			p++;
599b8b1ca5bSgrange 			entrysize = 0;
600b8b1ca5bSgrange 			break;
601b8b1ca5bSgrange 		case WDCEVENT_ATAPI_CMD:
602b8b1ca5bSgrange 			if (entrysize < 2 || p + 2 > total)
603b8b1ca5bSgrange 				return;
604b8b1ca5bSgrange 
605b8b1ca5bSgrange 			flags = (buf[p] << 8) + buf[p + 1];
606b8b1ca5bSgrange 			printf(": flags 0x%x", flags);
607b8b1ca5bSgrange 			if (flags & 0x0100)
608b8b1ca5bSgrange 				printf(" MEDIA");
609b8b1ca5bSgrange 			if (flags & 0x0080)
610b8b1ca5bSgrange 				printf(" SENSE");
611b8b1ca5bSgrange 			if (flags & 0x0040)
612b8b1ca5bSgrange 				printf(" DMA");
613b8b1ca5bSgrange 			if (flags & 0x0020)
614b8b1ca5bSgrange 				printf(" POLL");
615b8b1ca5bSgrange 			if (flags & 0x0004)
616b8b1ca5bSgrange 				printf(" TIMEOUT");
617b8b1ca5bSgrange 			if (flags & 0x0002)
618b8b1ca5bSgrange 				printf(" ATAPI");
619b8b1ca5bSgrange 
620b8b1ca5bSgrange 			p += 2;
621b8b1ca5bSgrange 			entrysize -= 2;
622b8b1ca5bSgrange 			break;
623b8b1ca5bSgrange 		case WDCEVENT_ATAPI_DONE:
624b8b1ca5bSgrange 			if (entrysize != 3 || p + 3 > total)
625b8b1ca5bSgrange 				return;
626b8b1ca5bSgrange 
627b8b1ca5bSgrange 			flags = (buf[p] << 8) + buf[p + 1];
628b8b1ca5bSgrange 			printf(": flags 0x%x", flags);
629b8b1ca5bSgrange 			if (flags & 0x0100)
630b8b1ca5bSgrange 				printf(" MEDIA");
631b8b1ca5bSgrange 			if (flags & 0x0080)
632b8b1ca5bSgrange 				printf(" SENSE");
633b8b1ca5bSgrange 			if (flags & 0x0040)
634b8b1ca5bSgrange 				printf(" DMA");
635b8b1ca5bSgrange 			if (flags & 0x0020)
636b8b1ca5bSgrange 				printf(" POLL");
637b8b1ca5bSgrange 			if (flags & 0x0004)
638b8b1ca5bSgrange 				printf(" TIMEOUT");
639b8b1ca5bSgrange 			if (flags & 0x0002)
640b8b1ca5bSgrange 				printf(" ATAPI");
641b8b1ca5bSgrange 
642b8b1ca5bSgrange 			printf(", error 0x%x", buf[p + 2]);
643b8b1ca5bSgrange 			switch (buf[p + 2]) {
644b8b1ca5bSgrange 			case 1:
645b8b1ca5bSgrange 				printf(" (sense)");
646b8b1ca5bSgrange 				break;
647b8b1ca5bSgrange 			case 2:
648b8b1ca5bSgrange 				printf(" (driver failure)");
649b8b1ca5bSgrange 				break;
650b8b1ca5bSgrange 			case 3:
651b8b1ca5bSgrange 				printf(" (timeout)");
652b8b1ca5bSgrange 				break;
653b8b1ca5bSgrange 			case 4:
654b8b1ca5bSgrange 				printf(" (busy)");
655b8b1ca5bSgrange 				break;
656b8b1ca5bSgrange 			case 5:
657b8b1ca5bSgrange 				printf(" (ATAPI sense)");
658b8b1ca5bSgrange 				break;
659b8b1ca5bSgrange 			case 8:
660b8b1ca5bSgrange 				printf(" (reset)");
661b8b1ca5bSgrange 				break;
662b8b1ca5bSgrange 			}
663b8b1ca5bSgrange 
664b8b1ca5bSgrange 			p += 3;
665b8b1ca5bSgrange 			entrysize  = 0;
666b8b1ca5bSgrange 			break;
667b8b1ca5bSgrange 		case WDCEVENT_ATA_LONG:
668b8b1ca5bSgrange 			if (entrysize != 7 || p + 7 > total)
669b8b1ca5bSgrange 				return;
670b8b1ca5bSgrange 
671b8b1ca5bSgrange 			printf(": ");
672b8b1ca5bSgrange 			switch (buf[p + 6]) {
673b8b1ca5bSgrange 			case WDCC_READDMA:
674b8b1ca5bSgrange 				printf("READ DMA");
675b8b1ca5bSgrange 				break;
676b8b1ca5bSgrange 			case WDCC_WRITEDMA:
677b8b1ca5bSgrange 				printf("WRITE DMA");
678b8b1ca5bSgrange 				break;
679b8b1ca5bSgrange 			default:
6805bb976d5Sgrange 				printf("CMD 0x%x", buf[p + 6]);
681b8b1ca5bSgrange 			}
682b8b1ca5bSgrange 			printf(" head %d, precomp %d, cyl_hi %d, "
683b8b1ca5bSgrange 			    "cyl_lo %d, sec %d, cnt %d",
684b8b1ca5bSgrange 			    buf[p], buf[p + 1], buf[p + 2], buf[p + 3],
685b8b1ca5bSgrange 			    buf[p + 4], buf[p + 5]);
686b8b1ca5bSgrange 
687b8b1ca5bSgrange 			p += 7;
688b8b1ca5bSgrange 			entrysize = 0;
689b8b1ca5bSgrange 			break;
690b8b1ca5bSgrange 		case WDCEVENT_REG:
691b8b1ca5bSgrange 			if (entrysize != 3 || p + 3 > total)
692b8b1ca5bSgrange 				return;
693b8b1ca5bSgrange 
694b8b1ca5bSgrange 			switch (buf[p]) {
695b8b1ca5bSgrange 			case 1:
696b8b1ca5bSgrange 				printf(": error");
697b8b1ca5bSgrange 				break;
698b8b1ca5bSgrange 			case 2:
699b8b1ca5bSgrange 				printf(": ireason");
700b8b1ca5bSgrange 				break;
701b8b1ca5bSgrange 			case 3:
702b8b1ca5bSgrange 				printf(": lba_lo");
703b8b1ca5bSgrange 				break;
704b8b1ca5bSgrange 			case 4:
705b8b1ca5bSgrange 				printf(": lba_mi");
706b8b1ca5bSgrange 				break;
707b8b1ca5bSgrange 			case 5:
708b8b1ca5bSgrange 				printf(": lba_hi");
709b8b1ca5bSgrange 				break;
710b8b1ca5bSgrange 			case 6:
711b8b1ca5bSgrange 				printf(": sdh");
712b8b1ca5bSgrange 				break;
713b8b1ca5bSgrange 			case 7:
714b8b1ca5bSgrange 				printf(": status");
715b8b1ca5bSgrange 				break;
716b8b1ca5bSgrange 			case 8:
717b8b1ca5bSgrange 				printf(": altstatus");
718b8b1ca5bSgrange 				break;
719b8b1ca5bSgrange 			default:
720b8b1ca5bSgrange 				printf(": unknown register %d", buf[p]);
721b8b1ca5bSgrange 			}
722b8b1ca5bSgrange 			printf(": 0x%x", (buf[p + 1] << 8) + buf[p + 2]);
723b8b1ca5bSgrange 
724b8b1ca5bSgrange 			p += 3;
725b8b1ca5bSgrange 			entrysize = 0;
726b8b1ca5bSgrange 			break;
727b8b1ca5bSgrange 		case WDCEVENT_ATA_EXT:
728b8b1ca5bSgrange 			if (entrysize != 9 || p + 9 > total)
729b8b1ca5bSgrange 				return;
730b8b1ca5bSgrange 
731b8b1ca5bSgrange 			printf(": ");
732b8b1ca5bSgrange 			switch (buf[p + 8]) {
733b8b1ca5bSgrange 			case WDCC_READDMA_EXT:
734b8b1ca5bSgrange 				printf("READ DMA EXT");
735b8b1ca5bSgrange 				break;
736b8b1ca5bSgrange 			case WDCC_WRITEDMA_EXT:
737b8b1ca5bSgrange 				printf("WRITE DMA EXT");
738b8b1ca5bSgrange 				break;
739b8b1ca5bSgrange 			default:
7405bb976d5Sgrange 				printf("CMD 0x%x", buf[p + 8]);
741b8b1ca5bSgrange 			}
742b8b1ca5bSgrange 			printf(" lba_hi1 %d, lba_hi2 %d, "
743b8b1ca5bSgrange 			    "lba_mi1 %d, lba_mi2 %d, lba_lo1 %d, lba_lo2 %d, "
744b8b1ca5bSgrange 			    "count1 %d, count2 %d",
745b8b1ca5bSgrange 			    buf[p], buf[p + 1], buf[p + 2], buf[p + 3],
746b8b1ca5bSgrange 			    buf[p + 4], buf[p + 5], buf[p + 6],
747b8b1ca5bSgrange 			    buf[p + 7]);
748b8b1ca5bSgrange 
749b8b1ca5bSgrange 			p += 9;
750b8b1ca5bSgrange 			entrysize = 0;
751b8b1ca5bSgrange 			break;
752b8b1ca5bSgrange 		}
753b8b1ca5bSgrange 
754b8b1ca5bSgrange 		if (entrysize > 0)
755b8b1ca5bSgrange 			printf(":");
756b8b1ca5bSgrange 		for (i = 0; i < entrysize; i++) {
757b8b1ca5bSgrange 			printf (" 0x%02x", buf[p]);
758b8b1ca5bSgrange 			if (++p >= total)
759b8b1ca5bSgrange 				break;
760b8b1ca5bSgrange 		}
761b8b1ca5bSgrange 		printf("\n");
762b8b1ca5bSgrange 	}
76352ffca01Scsapuntz 
76452ffca01Scsapuntz 	return;
765dc223e5cSgrange 
766dc223e5cSgrange usage:
767d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]);
768dc223e5cSgrange 	exit(1);
76952ffca01Scsapuntz }
77052ffca01Scsapuntz 
771239cdf63Scsapuntz /*
772239cdf63Scsapuntz  * device_identify:
773239cdf63Scsapuntz  *
774239cdf63Scsapuntz  *	Display the identity of the device
775239cdf63Scsapuntz  */
776239cdf63Scsapuntz void
device_identify(int argc,char * argv[])777bc52e260Sderaadt device_identify(int argc, char *argv[])
778239cdf63Scsapuntz {
779239cdf63Scsapuntz 	struct ataparams *inqbuf;
780239cdf63Scsapuntz 	struct atareq req;
7814bf7812dSmoritz 	char inbuf[DEV_BSIZE];
7824d2db249Sgrange 	u_int64_t capacity;
7834bf7812dSmoritz 	u_int8_t *s;
784239cdf63Scsapuntz 
78530520cf8Sgluk 	if (argc != 1)
786239cdf63Scsapuntz 		goto usage;
787239cdf63Scsapuntz 
788239cdf63Scsapuntz 	memset(&inbuf, 0, sizeof(inbuf));
789239cdf63Scsapuntz 	memset(&req, 0, sizeof(req));
790239cdf63Scsapuntz 
791239cdf63Scsapuntz 	inqbuf = (struct ataparams *) inbuf;
792239cdf63Scsapuntz 
793239cdf63Scsapuntz 	req.flags = ATACMD_READ;
794239cdf63Scsapuntz 	req.command = WDCC_IDENTIFY;
795239cdf63Scsapuntz 	req.databuf = (caddr_t) inbuf;
796239cdf63Scsapuntz 	req.datalen = sizeof(inbuf);
797239cdf63Scsapuntz 	req.timeout = 1000;
798239cdf63Scsapuntz 
799239cdf63Scsapuntz 	ata_command(&req);
800239cdf63Scsapuntz 
80156fefdbeScsapuntz 	if (BYTE_ORDER == BIG_ENDIAN) {
80256fefdbeScsapuntz 		swap16_multi((u_int16_t *)inbuf, 10);
80356fefdbeScsapuntz 		swap16_multi(((u_int16_t *)inbuf) + 20, 3);
80456fefdbeScsapuntz 		swap16_multi(((u_int16_t *)inbuf) + 47, sizeof(inbuf) / 2 - 47);
80556fefdbeScsapuntz 	}
806239cdf63Scsapuntz 
807239cdf63Scsapuntz 	if (!((inqbuf->atap_config & WDC_CFG_ATAPI_MASK) == WDC_CFG_ATAPI &&
808239cdf63Scsapuntz 	      ((inqbuf->atap_model[0] == 'N' &&
809239cdf63Scsapuntz 		  inqbuf->atap_model[1] == 'E') ||
810239cdf63Scsapuntz 	       (inqbuf->atap_model[0] == 'F' &&
811239cdf63Scsapuntz 		  inqbuf->atap_model[1] == 'X')))) {
81256fefdbeScsapuntz 		swap16_multi((u_int16_t *)(inqbuf->atap_model),
81356fefdbeScsapuntz 		    sizeof(inqbuf->atap_model) / 2);
81456fefdbeScsapuntz 		swap16_multi((u_int16_t *)(inqbuf->atap_serial),
815200eff2bSgluk 		    sizeof(inqbuf->atap_serial) / 2);
816200eff2bSgluk 		swap16_multi((u_int16_t *)(inqbuf->atap_revision),
817200eff2bSgluk 		    sizeof(inqbuf->atap_revision) / 2);
818239cdf63Scsapuntz 	}
819239cdf63Scsapuntz 
820239cdf63Scsapuntz 	/*
82130520cf8Sgluk 	 * Strip blanks off of the info strings.
822239cdf63Scsapuntz 	 */
823239cdf63Scsapuntz 
82430520cf8Sgluk 	for (s = &inqbuf->atap_model[sizeof(inqbuf->atap_model) - 1];
8254bf7812dSmoritz 	    s >= inqbuf->atap_model && *s == ' '; s--)
82630520cf8Sgluk 		*s = '\0';
827239cdf63Scsapuntz 
82830520cf8Sgluk 	for (s = &inqbuf->atap_revision[sizeof(inqbuf->atap_revision) - 1];
8294bf7812dSmoritz 	    s >= inqbuf->atap_revision && *s == ' '; s--)
83030520cf8Sgluk 		*s = '\0';
831239cdf63Scsapuntz 
83230520cf8Sgluk 	for (s = &inqbuf->atap_serial[sizeof(inqbuf->atap_serial) - 1];
8334bf7812dSmoritz 	    s >= inqbuf->atap_serial && *s == ' '; s--)
83430520cf8Sgluk 		*s = '\0';
835239cdf63Scsapuntz 
836239cdf63Scsapuntz 	printf("Model: %.*s, Rev: %.*s, Serial #: %.*s\n",
837239cdf63Scsapuntz 	    (int) sizeof(inqbuf->atap_model), inqbuf->atap_model,
838239cdf63Scsapuntz 	    (int) sizeof(inqbuf->atap_revision), inqbuf->atap_revision,
839239cdf63Scsapuntz 	    (int) sizeof(inqbuf->atap_serial), inqbuf->atap_serial);
840239cdf63Scsapuntz 
841239cdf63Scsapuntz 	printf("Device type: %s, %s\n", inqbuf->atap_config & WDC_CFG_ATAPI ?
842239cdf63Scsapuntz 	       "ATAPI" : "ATA", inqbuf->atap_config & ATA_CFG_FIXED ? "fixed" :
843239cdf63Scsapuntz 	       "removable");
844239cdf63Scsapuntz 
8454d2db249Sgrange 	if (inqbuf->atap_cmd2_en & ATAPI_CMD2_48AD)
8464d2db249Sgrange 		capacity = ((u_int64_t)inqbuf->atap_max_lba[3] << 48) |
8474d2db249Sgrange 		    ((u_int64_t)inqbuf->atap_max_lba[2] << 32) |
8484d2db249Sgrange 		    ((u_int64_t)inqbuf->atap_max_lba[1] << 16) |
8494d2db249Sgrange 		    (u_int64_t)inqbuf->atap_max_lba[0];
8504d2db249Sgrange 	else
8514d2db249Sgrange 		capacity = (inqbuf->atap_capacity[1] << 16) |
8524d2db249Sgrange 		    inqbuf->atap_capacity[0];
853239cdf63Scsapuntz 	printf("Cylinders: %d, heads: %d, sec/track: %d, total "
8544d2db249Sgrange 	    "sectors: %llu\n", inqbuf->atap_cylinders,
8554d2db249Sgrange 	    inqbuf->atap_heads, inqbuf->atap_sectors, capacity);
856239cdf63Scsapuntz 
8572832b9fdSgluk 	if ((inqbuf->atap_cmd_set2 & ATA_CMD2_RWQ) &&
8582832b9fdSgluk 	    (inqbuf->atap_queuedepth & WDC_QUEUE_DEPTH_MASK))
859239cdf63Scsapuntz 		printf("Device supports command queue depth of %d\n",
8602832b9fdSgluk 		    (inqbuf->atap_queuedepth & WDC_QUEUE_DEPTH_MASK) + 1);
861239cdf63Scsapuntz 
862239cdf63Scsapuntz 	printf("Device capabilities:\n");
863239cdf63Scsapuntz 	print_bitinfo("\t%s\n", inqbuf->atap_capabilities1, ata_caps);
864239cdf63Scsapuntz 
865239cdf63Scsapuntz 	if (inqbuf->atap_ata_major != 0 && inqbuf->atap_ata_major != 0xffff) {
866dc223e5cSgrange 		printf("Device supports the following standards:\n");
867239cdf63Scsapuntz 		print_bitinfo("%s ", inqbuf->atap_ata_major, ata_vers);
868239cdf63Scsapuntz 		printf("\n");
869239cdf63Scsapuntz 	}
870239cdf63Scsapuntz 
8716e60c95aSgluk 	if ((inqbuf->atap_cmd_set1 & WDC_CMD1_SEC) &&
8726e60c95aSgluk 	    inqbuf->atap_mpasswd_rev != 0 &&
8736e60c95aSgluk 	    inqbuf->atap_mpasswd_rev != 0xffff)
8746e60c95aSgluk 		printf("Master password revision code 0x%04x\n",
8756e60c95aSgluk 		    inqbuf->atap_mpasswd_rev);
8766e60c95aSgluk 
877239cdf63Scsapuntz 	if (inqbuf->atap_cmd_set1 != 0 && inqbuf->atap_cmd_set1 != 0xffff &&
878239cdf63Scsapuntz 	    inqbuf->atap_cmd_set2 != 0 && inqbuf->atap_cmd_set2 != 0xffff) {
8793ded539eSderaadt 		printf("Device supports the following command sets:\n");
880239cdf63Scsapuntz 		print_bitinfo("\t%s\n", inqbuf->atap_cmd_set1, ata_cmd_set1);
881239cdf63Scsapuntz 		print_bitinfo("\t%s\n", inqbuf->atap_cmd_set2, ata_cmd_set2);
8823ded539eSderaadt 		print_bitinfo("\t%s\n", inqbuf->atap_cmd_ext, ata_cmd_ext);
883239cdf63Scsapuntz 	}
884239cdf63Scsapuntz 
885239cdf63Scsapuntz 	if (inqbuf->atap_cmd_def != 0 && inqbuf->atap_cmd_def != 0xffff) {
88630520cf8Sgluk 		printf("Device has enabled the following command "
88730520cf8Sgluk 		    "sets/features:\n");
8883ded539eSderaadt 		print_bitinfo("\t%s\n", inqbuf->atap_cmd1_en, ata_cmd_set1);
8893ded539eSderaadt 		print_bitinfo("\t%s\n", inqbuf->atap_cmd2_en, ata_cmd_set2);
890239cdf63Scsapuntz 	}
891239cdf63Scsapuntz 
892239cdf63Scsapuntz 	return;
893239cdf63Scsapuntz 
894239cdf63Scsapuntz usage:
895d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]);
896239cdf63Scsapuntz 	exit(1);
897239cdf63Scsapuntz }
898239cdf63Scsapuntz 
899239cdf63Scsapuntz /*
900239cdf63Scsapuntz  * device idle:
901239cdf63Scsapuntz  *
902239cdf63Scsapuntz  * issue the IDLE IMMEDIATE command to the drive
903239cdf63Scsapuntz  */
904239cdf63Scsapuntz void
device_idle(int argc,char * argv[])905bc52e260Sderaadt device_idle(int argc, char *argv[])
906239cdf63Scsapuntz {
907239cdf63Scsapuntz 	struct atareq req;
908239cdf63Scsapuntz 
90930520cf8Sgluk 	if (argc != 1)
910239cdf63Scsapuntz 		goto usage;
911239cdf63Scsapuntz 
912239cdf63Scsapuntz 	memset(&req, 0, sizeof(req));
913239cdf63Scsapuntz 
91430520cf8Sgluk 	if (strcmp(argv[0], "idle") == 0)
915239cdf63Scsapuntz 		req.command = WDCC_IDLE_IMMED;
91630520cf8Sgluk 	else if (strcmp(argv[0], "standby") == 0)
917239cdf63Scsapuntz 		req.command = WDCC_STANDBY_IMMED;
918239cdf63Scsapuntz 	else
919239cdf63Scsapuntz 		req.command = WDCC_SLEEP;
920239cdf63Scsapuntz 
921239cdf63Scsapuntz 	req.timeout = 1000;
922239cdf63Scsapuntz 
923239cdf63Scsapuntz 	ata_command(&req);
924239cdf63Scsapuntz 
925239cdf63Scsapuntz 	return;
926239cdf63Scsapuntz usage:
927d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]);
928239cdf63Scsapuntz 	exit(1);
929239cdf63Scsapuntz }
930239cdf63Scsapuntz 
931239cdf63Scsapuntz /*
9326e60c95aSgluk  * SECURITY SET PASSWORD command
9336e60c95aSgluk  */
9346e60c95aSgluk void
device_sec_setpass(int argc,char * argv[])9356e60c95aSgluk device_sec_setpass(int argc, char *argv[])
9366e60c95aSgluk {
9376e60c95aSgluk 	struct atareq req;
9386e60c95aSgluk 	struct sec_password pwd;
9396e60c95aSgluk 	char *pass, inbuf[DEV_BSIZE];
9406e60c95aSgluk 	struct ataparams *inqbuf = (struct ataparams *)inbuf;
9416e60c95aSgluk 
9426e60c95aSgluk 	if (argc < 2)
9436e60c95aSgluk 		goto usage;
9446e60c95aSgluk 
9456e60c95aSgluk 	memset(&pwd, 0, sizeof(pwd));
9466e60c95aSgluk 
9476e60c95aSgluk 	if (strcmp(argv[1], "user") == 0 && argc == 3)
9486e60c95aSgluk 		pwd.ctrl |= SEC_PASSWORD_USER;
9496e60c95aSgluk 	else if (strcmp(argv[1], "master") == 0 && argc == 2)
9506e60c95aSgluk 		pwd.ctrl |= SEC_PASSWORD_MASTER;
9516e60c95aSgluk 	else
9526e60c95aSgluk 		goto usage;
953dc223e5cSgrange 	if (argc == 3) {
9546e60c95aSgluk 		if (strcmp(argv[2], "high") == 0)
9556e60c95aSgluk 			pwd.ctrl |= SEC_LEVEL_HIGH;
9566e60c95aSgluk 		else if (strcmp(argv[2], "maximum") == 0)
9576e60c95aSgluk 			pwd.ctrl |= SEC_LEVEL_MAX;
9586e60c95aSgluk 		else
9596e60c95aSgluk 			goto usage;
960dc223e5cSgrange 	}
9616e60c95aSgluk 
9626e60c95aSgluk 	/*
9636e60c95aSgluk 	 * Issue IDENTIFY command to obtain master password
9646e60c95aSgluk 	 * revision code and decrement its value.
9656e60c95aSgluk 	 * The valid revision codes are 0x0001 through 0xfffe.
9664510335dSgluk 	 * If the device returns 0x0000 or 0xffff as a revision
9676e60c95aSgluk 	 * code then the master password revision code is not
9686e60c95aSgluk 	 * supported so don't touch it.
9696e60c95aSgluk 	 */
9706e60c95aSgluk 	memset(&inbuf, 0, sizeof(inbuf));
9716e60c95aSgluk 	memset(&req, 0, sizeof(req));
9726e60c95aSgluk 
9736e60c95aSgluk 	req.command = WDCC_IDENTIFY;
9746e60c95aSgluk 	req.timeout = 1000;
9756e60c95aSgluk 	req.flags = ATACMD_READ;
9766e60c95aSgluk 	req.databuf = (caddr_t)inbuf;
9776e60c95aSgluk 	req.datalen = sizeof(inbuf);
9786e60c95aSgluk 
9796e60c95aSgluk 	ata_command(&req);
9806e60c95aSgluk 
9816e60c95aSgluk 	pwd.revision = inqbuf->atap_mpasswd_rev;
9826e60c95aSgluk 	if (pwd.revision != 0 && pwd.revision != 0xffff && --pwd.revision == 0)
9836e60c95aSgluk 		pwd.revision = 0xfffe;
9846e60c95aSgluk 
9856e60c95aSgluk 	pass = sec_getpass(pwd.ctrl & SEC_PASSWORD_MASTER, 1);
9866e60c95aSgluk 	memcpy(pwd.password, pass, strlen(pass));
9876e60c95aSgluk 
9886e60c95aSgluk 	memset(&req, 0, sizeof(req));
9896e60c95aSgluk 
9906e60c95aSgluk 	req.command = ATA_SEC_SET_PASSWORD;
9916e60c95aSgluk 	req.timeout = 1000;
9926e60c95aSgluk 	req.flags = ATACMD_WRITE;
9936e60c95aSgluk 	req.databuf = (caddr_t)&pwd;
9946e60c95aSgluk 	req.datalen = sizeof(pwd);
9956e60c95aSgluk 
9966e60c95aSgluk 	ata_command(&req);
9976e60c95aSgluk 
9986e60c95aSgluk 	return;
9996e60c95aSgluk usage:
1000d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s user high | maximum\n",
10016e60c95aSgluk 	    __progname, argv[0]);
1002d3fa79dbSsobrado 	fprintf(stderr, "       %s device %s master\n", __progname, argv[0]);
1003dc223e5cSgrange 	exit(1);
10046e60c95aSgluk }
10056e60c95aSgluk 
10066e60c95aSgluk /*
10076e60c95aSgluk  * SECURITY UNLOCK command
10086e60c95aSgluk  */
10096e60c95aSgluk void
device_sec_unlock(int argc,char * argv[])10106e60c95aSgluk device_sec_unlock(int argc, char *argv[])
10116e60c95aSgluk {
10126e60c95aSgluk 	struct atareq req;
10136e60c95aSgluk 	struct sec_password pwd;
10146e60c95aSgluk 	char *pass;
10156e60c95aSgluk 
10166e60c95aSgluk 	if (argc != 2)
10176e60c95aSgluk 		goto usage;
10186e60c95aSgluk 
10196e60c95aSgluk 	memset(&pwd, 0, sizeof(pwd));
10206e60c95aSgluk 
10216e60c95aSgluk 	if (strcmp(argv[1], "user") == 0)
10226e60c95aSgluk 		pwd.ctrl |= SEC_PASSWORD_USER;
10236e60c95aSgluk 	else if (strcmp(argv[1], "master") == 0)
10246e60c95aSgluk 		pwd.ctrl |= SEC_PASSWORD_MASTER;
10256e60c95aSgluk 	else
10266e60c95aSgluk 		goto usage;
10276e60c95aSgluk 
10286e60c95aSgluk 	pass = sec_getpass(pwd.ctrl & SEC_PASSWORD_MASTER, 0);
10296e60c95aSgluk 	memcpy(pwd.password, pass, strlen(pass));
10306e60c95aSgluk 
10316e60c95aSgluk 	memset(&req, 0, sizeof(req));
10326e60c95aSgluk 
10336e60c95aSgluk 	req.command = ATA_SEC_UNLOCK;
10346e60c95aSgluk 	req.timeout = 1000;
10356e60c95aSgluk 	req.flags = ATACMD_WRITE;
10366e60c95aSgluk 	req.databuf = (caddr_t)&pwd;
10376e60c95aSgluk 	req.datalen = sizeof(pwd);
10386e60c95aSgluk 
10396e60c95aSgluk 	ata_command(&req);
10406e60c95aSgluk 
10416e60c95aSgluk 	return;
10426e60c95aSgluk usage:
1043d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s user | master\n", __progname,
10446e60c95aSgluk 	    argv[0]);
1045dc223e5cSgrange 	exit(1);
10466e60c95aSgluk }
10476e60c95aSgluk 
10486e60c95aSgluk /*
10496e60c95aSgluk  * SECURITY ERASE UNIT command
10506e60c95aSgluk  */
10516e60c95aSgluk void
device_sec_erase(int argc,char * argv[])10526e60c95aSgluk device_sec_erase(int argc, char *argv[])
10536e60c95aSgluk {
10546e60c95aSgluk 	struct atareq req;
10556e60c95aSgluk 	struct sec_password pwd;
10566e60c95aSgluk 	char *pass;
10576e60c95aSgluk 
10586e60c95aSgluk 	if (argc < 2)
10596e60c95aSgluk 		goto usage;
10606e60c95aSgluk 
10616e60c95aSgluk 	memset(&pwd, 0, sizeof(pwd));
10626e60c95aSgluk 
10636e60c95aSgluk 	if (strcmp(argv[1], "user") == 0)
10646e60c95aSgluk 		pwd.ctrl |= SEC_PASSWORD_USER;
10656e60c95aSgluk 	else if (strcmp(argv[1], "master") == 0)
10666e60c95aSgluk 		pwd.ctrl |= SEC_PASSWORD_MASTER;
10676e60c95aSgluk 	else
10686e60c95aSgluk 		goto usage;
10696e60c95aSgluk 	if (argc == 2)
10706e60c95aSgluk 		pwd.ctrl |= SEC_ERASE_NORMAL;
10716e60c95aSgluk 	else if (argc == 3 && strcmp(argv[2], "enhanced") == 0)
10726e60c95aSgluk 		pwd.ctrl |= SEC_ERASE_ENHANCED;
10736e60c95aSgluk 	else
10746e60c95aSgluk 		goto usage;
10756e60c95aSgluk 
10766e60c95aSgluk 	pass = sec_getpass(pwd.ctrl & SEC_PASSWORD_MASTER, 0);
10776e60c95aSgluk 	memcpy(pwd.password, pass, strlen(pass));
10786e60c95aSgluk 
10796e60c95aSgluk 	 /* Issue SECURITY ERASE PREPARE command before */
10806e60c95aSgluk 	memset(&req, 0, sizeof(req));
10816e60c95aSgluk 
10826e60c95aSgluk 	req.command = ATA_SEC_ERASE_PREPARE;
10836e60c95aSgluk 	req.timeout = 1000;
10846e60c95aSgluk 
10856e60c95aSgluk 	ata_command(&req);
10866e60c95aSgluk 
10876e60c95aSgluk 	memset(&req, 0, sizeof(req));
10886e60c95aSgluk 
10896e60c95aSgluk 	req.command = ATA_SEC_ERASE_UNIT;
10906e60c95aSgluk 	req.timeout = 1000;
10916e60c95aSgluk 	req.flags = ATACMD_WRITE;
10926e60c95aSgluk 	req.databuf = (caddr_t)&pwd;
10936e60c95aSgluk 	req.datalen = sizeof(pwd);
10946e60c95aSgluk 
10956e60c95aSgluk 	ata_command(&req);
10966e60c95aSgluk 
10976e60c95aSgluk 	return;
10986e60c95aSgluk usage:
1099d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s user | master [enhanced]\n",
11006e60c95aSgluk 	    __progname, argv[0]);
1101dc223e5cSgrange 	exit(1);
11026e60c95aSgluk }
11036e60c95aSgluk 
11046e60c95aSgluk /*
11056e60c95aSgluk  * SECURITY FREEZE LOCK command
11066e60c95aSgluk  */
11076e60c95aSgluk void
device_sec_freeze(int argc,char * argv[])11086e60c95aSgluk device_sec_freeze(int argc, char *argv[])
11096e60c95aSgluk {
11106e60c95aSgluk 	struct atareq req;
11116e60c95aSgluk 
11126e60c95aSgluk 	if (argc != 1)
11136e60c95aSgluk 		goto usage;
11146e60c95aSgluk 
11156e60c95aSgluk 	memset(&req, 0, sizeof(req));
11166e60c95aSgluk 
11176e60c95aSgluk 	req.command = ATA_SEC_FREEZE_LOCK;
11186e60c95aSgluk 	req.timeout = 1000;
11196e60c95aSgluk 
11206e60c95aSgluk 	ata_command(&req);
11216e60c95aSgluk 
11226e60c95aSgluk 	return;
11236e60c95aSgluk usage:
1124d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]);
1125dc223e5cSgrange 	exit(1);
11266e60c95aSgluk }
11276e60c95aSgluk 
11286e60c95aSgluk /*
11296e60c95aSgluk  * SECURITY DISABLE PASSWORD command
11306e60c95aSgluk  */
11316e60c95aSgluk void
device_sec_disablepass(int argc,char * argv[])11326e60c95aSgluk device_sec_disablepass(int argc, char *argv[])
11336e60c95aSgluk {
11346e60c95aSgluk 	struct atareq req;
11356e60c95aSgluk 	struct sec_password pwd;
11366e60c95aSgluk 	char *pass;
11376e60c95aSgluk 
11386e60c95aSgluk 	if (argc != 2)
11396e60c95aSgluk 		goto usage;
11406e60c95aSgluk 
11416e60c95aSgluk 	memset(&pwd, 0, sizeof(pwd));
11426e60c95aSgluk 
11436e60c95aSgluk 	if (strcmp(argv[1], "user") == 0)
11446e60c95aSgluk 		pwd.ctrl |= SEC_PASSWORD_USER;
11456e60c95aSgluk 	else if (strcmp(argv[1], "master") == 0)
11466e60c95aSgluk 		pwd.ctrl |= SEC_PASSWORD_MASTER;
11476e60c95aSgluk 	else
11486e60c95aSgluk 		goto usage;
11496e60c95aSgluk 
11506e60c95aSgluk 	pass = sec_getpass(pwd.ctrl & SEC_PASSWORD_MASTER, 0);
11516e60c95aSgluk 	memcpy(pwd.password, pass, strlen(pass));
11526e60c95aSgluk 
11536e60c95aSgluk 	memset(&req, 0, sizeof(req));
11546e60c95aSgluk 
11556e60c95aSgluk 	req.command = ATA_SEC_DISABLE_PASSWORD;
11566e60c95aSgluk 	req.timeout = 1000;
11576e60c95aSgluk 	req.flags = ATACMD_WRITE;
11586e60c95aSgluk 	req.databuf = (caddr_t)&pwd;
11596e60c95aSgluk 	req.datalen = sizeof(pwd);
11606e60c95aSgluk 
11616e60c95aSgluk 	ata_command(&req);
11626e60c95aSgluk 
11636e60c95aSgluk 	return;
11646e60c95aSgluk usage:
1165d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s user | master\n", __progname,
11666e60c95aSgluk 	    argv[0]);
1167dc223e5cSgrange 	exit(1);
11686e60c95aSgluk }
11696e60c95aSgluk 
11706e60c95aSgluk char *
sec_getpass(int ident,int confirm)11716e60c95aSgluk sec_getpass(int ident, int confirm)
11726e60c95aSgluk {
1173dc223e5cSgrange 	char *pass, buf[33];
11746e60c95aSgluk 
11756e60c95aSgluk 	if ((pass = getpass(ident ? "Master password:" :
11766e60c95aSgluk 	    "User password:")) == NULL)
11776e60c95aSgluk 		err(1, "getpass()");
11786e60c95aSgluk 	if (strlen(pass) > 32)
11796e60c95aSgluk 		errx(1, "password too long");
11806e60c95aSgluk 	if (confirm) {
1181dc223e5cSgrange 		strlcpy(buf, pass, sizeof(buf));
11826e60c95aSgluk 		if ((pass = getpass(ident ? "Retype master password:" :
11836e60c95aSgluk 		    "Retype user password:")) == NULL)
11846e60c95aSgluk 			err(1, "getpass()");
1185dc223e5cSgrange 		if (strcmp(pass, buf) != 0)
11866e60c95aSgluk 			errx(1, "password mismatch");
11876e60c95aSgluk 	}
11886e60c95aSgluk 
1189dc223e5cSgrange 	return (pass);
11906e60c95aSgluk }
11916e60c95aSgluk 
11926e60c95aSgluk /*
119330520cf8Sgluk  * SMART ENABLE OPERATIONS command
11943ded539eSderaadt  */
11953ded539eSderaadt void
device_smart_enable(int argc,char * argv[])1196bc52e260Sderaadt device_smart_enable(int argc, char *argv[])
11973ded539eSderaadt {
11983ded539eSderaadt 	struct atareq req;
11993ded539eSderaadt 
120030520cf8Sgluk 	if (argc != 1)
12013ded539eSderaadt 		goto usage;
12023ded539eSderaadt 
12033ded539eSderaadt 	memset(&req, 0, sizeof(req));
12043ded539eSderaadt 
12053ded539eSderaadt 	req.command = ATAPI_SMART;
120630520cf8Sgluk 	req.cylinder = 0xc24f;
12073ded539eSderaadt 	req.timeout = 1000;
120847f8c6e8Sgluk 	req.features = ATA_SMART_EN;
12093ded539eSderaadt 
12103ded539eSderaadt 	ata_command(&req);
12113ded539eSderaadt 
12123ded539eSderaadt 	return;
12133ded539eSderaadt usage:
1214d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]);
12153ded539eSderaadt 	exit(1);
12163ded539eSderaadt }
12173ded539eSderaadt 
12183ded539eSderaadt /*
121930520cf8Sgluk  * SMART DISABLE OPERATIONS command
122030520cf8Sgluk  */
122130520cf8Sgluk void
device_smart_disable(int argc,char * argv[])1222bc52e260Sderaadt device_smart_disable(int argc, char *argv[])
122330520cf8Sgluk {
122430520cf8Sgluk 	struct atareq req;
122530520cf8Sgluk 
122630520cf8Sgluk 	if (argc != 1)
122730520cf8Sgluk 		goto usage;
122830520cf8Sgluk 
122930520cf8Sgluk 	memset(&req, 0, sizeof(req));
123030520cf8Sgluk 
123130520cf8Sgluk 	req.command = ATAPI_SMART;
123230520cf8Sgluk 	req.cylinder = 0xc24f;
123330520cf8Sgluk 	req.timeout = 1000;
123447f8c6e8Sgluk 	req.features = ATA_SMART_DS;
123530520cf8Sgluk 
123630520cf8Sgluk 	ata_command(&req);
123730520cf8Sgluk 
123830520cf8Sgluk 	return;
123930520cf8Sgluk usage:
1240d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]);
124130520cf8Sgluk 	exit(1);
124230520cf8Sgluk }
124330520cf8Sgluk 
124430520cf8Sgluk /*
124530520cf8Sgluk  * SMART STATUS command
124630520cf8Sgluk  */
124730520cf8Sgluk void
device_smart_status(int argc,char * argv[])1248bc52e260Sderaadt device_smart_status(int argc, char *argv[])
124930520cf8Sgluk {
125030520cf8Sgluk 	struct atareq req;
125130520cf8Sgluk 
125230520cf8Sgluk 	if (argc != 1)
125330520cf8Sgluk 		goto usage;
125430520cf8Sgluk 
125530520cf8Sgluk 	memset(&req, 0, sizeof(req));
125630520cf8Sgluk 
125730520cf8Sgluk 	req.command = ATAPI_SMART;
125830520cf8Sgluk 	req.cylinder = 0xc24f;
125930520cf8Sgluk 	req.timeout = 1000;
126047f8c6e8Sgluk 	req.features = ATA_SMART_STATUS;
126130520cf8Sgluk 
126230520cf8Sgluk 	ata_command(&req);
126330520cf8Sgluk 
126430520cf8Sgluk 	if (req.cylinder == 0xc24f)
126530520cf8Sgluk 		printf("No SMART threshold exceeded\n");
126630520cf8Sgluk 	else if (req.cylinder == 0x2cf4) {
1267dc223e5cSgrange 		errx(2, "SMART threshold exceeded!");
126830520cf8Sgluk 	} else {
1269dc223e5cSgrange 		errx(1, "Unknown response %02x!", req.cylinder);
127030520cf8Sgluk 	}
127130520cf8Sgluk 
127230520cf8Sgluk 	return;
127330520cf8Sgluk usage:
1274d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]);
127530520cf8Sgluk 	exit(1);
127630520cf8Sgluk }
127730520cf8Sgluk 
127830520cf8Sgluk /*
127930520cf8Sgluk  * SMART ENABLE/DISABLE ATTRIBUTE AUTOSAVE command
128030520cf8Sgluk  */
128130520cf8Sgluk void
device_smart_autosave(int argc,char * argv[])1282bc52e260Sderaadt device_smart_autosave(int argc, char *argv[])
128330520cf8Sgluk {
128430520cf8Sgluk 	struct atareq req;
128530520cf8Sgluk 	int val;
128630520cf8Sgluk 
128730520cf8Sgluk 	if (argc != 2)
128830520cf8Sgluk 		goto usage;
128930520cf8Sgluk 
129030520cf8Sgluk 	memset(&req, 0, sizeof(req));
129130520cf8Sgluk 
129230520cf8Sgluk 	req.command = ATAPI_SMART;
129330520cf8Sgluk 	req.cylinder = 0xc24f;
129430520cf8Sgluk 	req.timeout = 1000;
129547f8c6e8Sgluk 	req.features = ATA_SMART_AUTOSAVE;
129630520cf8Sgluk 	if ((val = strtoval(argv[1], smart_autosave)) == -1)
129730520cf8Sgluk 		goto usage;
129830520cf8Sgluk 	req.sec_num = val;
129930520cf8Sgluk 
130030520cf8Sgluk 	ata_command(&req);
130130520cf8Sgluk 
130230520cf8Sgluk 	return;
130330520cf8Sgluk usage:
1304d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s enable | disable\n", __progname,
130530520cf8Sgluk 	    argv[0]);
130630520cf8Sgluk 	exit(1);
130730520cf8Sgluk }
130830520cf8Sgluk 
130930520cf8Sgluk /*
131030520cf8Sgluk  * SMART EXECUTE OFF-LINE IMMEDIATE command
131130520cf8Sgluk  */
131230520cf8Sgluk void
device_smart_offline(int argc,char * argv[])1313bc52e260Sderaadt device_smart_offline(int argc, char *argv[])
131430520cf8Sgluk {
131530520cf8Sgluk 	struct atareq req;
131630520cf8Sgluk 	int val;
131730520cf8Sgluk 
131830520cf8Sgluk 	if (argc != 2)
131930520cf8Sgluk 		goto usage;
132030520cf8Sgluk 
132130520cf8Sgluk 	memset(&req, 0, sizeof(req));
132230520cf8Sgluk 
132330520cf8Sgluk 	req.command = ATAPI_SMART;
132430520cf8Sgluk 	req.cylinder = 0xc24f;
132530520cf8Sgluk 	req.timeout = 1000;
132647f8c6e8Sgluk 	req.features = ATA_SMART_OFFLINE;
132730520cf8Sgluk 	if ((val = strtoval(argv[1], smart_offline)) == -1)
132830520cf8Sgluk 		goto usage;
132930520cf8Sgluk 	req.sec_num = val;
133030520cf8Sgluk 
133130520cf8Sgluk 	ata_command(&req);
133230520cf8Sgluk 
133330520cf8Sgluk 	return;
133430520cf8Sgluk usage:
1335d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s subcommand\n", __progname,
133630520cf8Sgluk 	    argv[0]);
133730520cf8Sgluk 	exit(1);
133830520cf8Sgluk }
133930520cf8Sgluk 
134030520cf8Sgluk /*
134130520cf8Sgluk  * SMART READ DATA command
134230520cf8Sgluk  */
134330520cf8Sgluk void
device_smart_read(int argc,char * argv[])1344bc52e260Sderaadt device_smart_read(int argc, char *argv[])
134530520cf8Sgluk {
134630520cf8Sgluk 	struct atareq req;
134730520cf8Sgluk 	struct smart_read data;
134830520cf8Sgluk 
134930520cf8Sgluk 	if (argc != 1)
135030520cf8Sgluk 		goto usage;
135130520cf8Sgluk 
135230520cf8Sgluk 	memset(&req, 0, sizeof(req));
135330520cf8Sgluk 	memset(&data, 0, sizeof(data));
135430520cf8Sgluk 
135530520cf8Sgluk 	req.command = ATAPI_SMART;
135630520cf8Sgluk 	req.cylinder = 0xc24f;
135730520cf8Sgluk 	req.timeout = 1000;
135847f8c6e8Sgluk 	req.features = ATA_SMART_READ;
135930520cf8Sgluk 	req.flags = ATACMD_READ;
136030520cf8Sgluk 	req.databuf = (caddr_t)&data;
136130520cf8Sgluk 	req.datalen = sizeof(data);
136230520cf8Sgluk 
136330520cf8Sgluk 	ata_command(&req);
136430520cf8Sgluk 
1365dc223e5cSgrange 	if (smart_cksum((u_int8_t *)&data, sizeof(data)) != 0)
1366dc223e5cSgrange 		errx(1, "Checksum mismatch");
136730520cf8Sgluk 
136830520cf8Sgluk 	printf("Off-line data collection:\n");
136930520cf8Sgluk 	printf("    status: %s\n",
1370be0cee2fSsemarie 	    valtostr(data.offstat & 0x7f, smart_offstat, "?"));
137130520cf8Sgluk 	printf("    activity completion time: %d seconds\n",
137230520cf8Sgluk 	    letoh16(data.time));
137330520cf8Sgluk 	printf("    capabilities:\n");
137430520cf8Sgluk 	print_bitinfo("\t%s\n", data.offcap, smart_offcap);
137530520cf8Sgluk 	printf("Self-test execution:\n");
137630520cf8Sgluk 	printf("    status: %s\n", valtostr(SMART_SELFSTAT_STAT(data.selfstat),
1377be0cee2fSsemarie 	    smart_selfstat, "?"));
137830520cf8Sgluk 	if (SMART_SELFSTAT_STAT(data.selfstat) == SMART_SELFSTAT_PROGRESS)
137930520cf8Sgluk 		printf("remains %d%% of total time\n",
138030520cf8Sgluk 		    SMART_SELFSTAT_PCNT(data.selfstat));
138130520cf8Sgluk 	printf("    recommended polling time:\n");
138230520cf8Sgluk 	printf("\tshort routine: %d minutes\n", data.shtime);
138330520cf8Sgluk 	printf("\textended routine: %d minutes\n", data.extime);
138430520cf8Sgluk 	printf("SMART capabilities:\n");
138530520cf8Sgluk 	print_bitinfo("    %s\n", letoh16(data.smartcap), smart_smartcap);
138630520cf8Sgluk 	printf("Error logging: ");
138730520cf8Sgluk 	if (data.errcap & SMART_ERRCAP_ERRLOG)
138830520cf8Sgluk 		printf("supported\n");
138930520cf8Sgluk 	else
139030520cf8Sgluk 		printf("not supported\n");
139130520cf8Sgluk 
139230520cf8Sgluk 	return;
139330520cf8Sgluk usage:
1394d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]);
139530520cf8Sgluk 	exit(1);
139630520cf8Sgluk }
139730520cf8Sgluk 
139830520cf8Sgluk /*
139930520cf8Sgluk  * SMART READ LOG command
140030520cf8Sgluk  */
140130520cf8Sgluk void
device_smart_readlog(int argc,char * argv[])1402bc52e260Sderaadt device_smart_readlog(int argc, char *argv[])
140330520cf8Sgluk {
140430520cf8Sgluk 	struct atareq req;
140530520cf8Sgluk 	int val;
140630520cf8Sgluk 	u_int8_t inbuf[DEV_BSIZE];
140730520cf8Sgluk 
140830520cf8Sgluk 	if (argc != 2)
140930520cf8Sgluk 		goto usage;
141030520cf8Sgluk 
141130520cf8Sgluk 	memset(&req, 0, sizeof(req));
141230520cf8Sgluk 	memset(&inbuf, 0, sizeof(inbuf));
141330520cf8Sgluk 
141430520cf8Sgluk 	req.command = ATAPI_SMART;
141530520cf8Sgluk 	req.cylinder = 0xc24f;
141630520cf8Sgluk 	req.timeout = 1000;
141747f8c6e8Sgluk 	req.features = ATA_SMART_READLOG;
141830520cf8Sgluk 	req.flags = ATACMD_READ;
141930520cf8Sgluk 	req.sec_count = 1;
142030520cf8Sgluk 	req.databuf = (caddr_t)inbuf;
142130520cf8Sgluk 	req.datalen = sizeof(inbuf);
142230520cf8Sgluk 	if ((val = strtoval(argv[1], smart_readlog)) == -1)
142330520cf8Sgluk 		goto usage;
142430520cf8Sgluk 	req.sec_num = val;
142530520cf8Sgluk 
142630520cf8Sgluk 	ata_command(&req);
142730520cf8Sgluk 
142830520cf8Sgluk 	if (strcmp(argv[1], "directory") == 0) {
142930520cf8Sgluk 		struct smart_log_dir *data = (struct smart_log_dir *)inbuf;
143030520cf8Sgluk 		int i;
143130520cf8Sgluk 
143230520cf8Sgluk 		if (data->version != SMART_LOG_MSECT) {
143330520cf8Sgluk 			printf("Device doesn't support multi-sector logs\n");
143430520cf8Sgluk 			return;
143530520cf8Sgluk 		}
143630520cf8Sgluk 
143730520cf8Sgluk 		for (i = 0; i < 255; i++)
143830520cf8Sgluk 			printf("Log address %d: %d sectors\n", i + 1,
143930520cf8Sgluk 			    data->entry[i].sec_num);
144030520cf8Sgluk 	} else if (strcmp(argv[1], "summary") == 0) {
144130520cf8Sgluk 		struct smart_log_sum *data = (struct smart_log_sum *)inbuf;
144230520cf8Sgluk 		int i, n, nerr;
144330520cf8Sgluk 
1444dc223e5cSgrange 		if (smart_cksum(inbuf, sizeof(inbuf)) != 0)
1445dc223e5cSgrange 			errx(1, "Checksum mismatch");
144630520cf8Sgluk 
144730520cf8Sgluk 		if (data->index == 0) {
144830520cf8Sgluk 			printf("No log entries\n");
144930520cf8Sgluk 			return;
145030520cf8Sgluk 		}
145130520cf8Sgluk 
145230520cf8Sgluk 		nerr = letoh16(data->err_cnt);
145330520cf8Sgluk 		printf("Error count: %d\n\n", nerr);
145430520cf8Sgluk 		/*
145530520cf8Sgluk 		 * Five error log data structures form a circular
145630520cf8Sgluk 		 * buffer. data->index points to the most recent
145730520cf8Sgluk 		 * record and err_cnt contains total error number.
145830520cf8Sgluk 		 * We pass from the most recent record to the
145930520cf8Sgluk 		 * latest one.
146030520cf8Sgluk 		 */
146130520cf8Sgluk 		i = data->index - 1;
146230520cf8Sgluk 		n = 0;
146330520cf8Sgluk 		do {
146430520cf8Sgluk 			printf("Error %d:\n", n + 1);
146530520cf8Sgluk 			smart_print_errdata(&data->errdata[i--]);
146630520cf8Sgluk 			if (i == -1)
146730520cf8Sgluk 				i = 4;
146830520cf8Sgluk 		} while (++n < (nerr > 5 ? 5 : nerr));
146930520cf8Sgluk 	} else if (strcmp(argv[1], "comp") == 0) {
147030520cf8Sgluk 		struct smart_log_comp *data = (struct smart_log_comp *)inbuf;
147130520cf8Sgluk 		u_int8_t *newbuf;
147230520cf8Sgluk 		int i, n, nerr, nsect;
147330520cf8Sgluk 
1474dc223e5cSgrange 		if (smart_cksum(inbuf, sizeof(inbuf)) != 0)
1475dc223e5cSgrange 			errx(1, "Checksum mismatch");
147630520cf8Sgluk 
147730520cf8Sgluk 		if (data->index == 0) {
147830520cf8Sgluk 			printf("No log entries\n");
147930520cf8Sgluk 			return;
148030520cf8Sgluk 		}
148130520cf8Sgluk 
148230520cf8Sgluk 		i = data->index - 1;
148330520cf8Sgluk 		nerr = letoh16(data->err_cnt);
148430520cf8Sgluk 		printf("Error count: %d\n", nerr);
148530520cf8Sgluk 		/*
148630520cf8Sgluk 		 * From the first sector we obtain total error number
148730520cf8Sgluk 		 * and calculate necessary number of sectors to read.
148830520cf8Sgluk 		 * All read error data structures form a circular
148930520cf8Sgluk 		 * buffer and we pass from the most recent record to
149030520cf8Sgluk 		 * the latest one.
149130520cf8Sgluk 		 */
149230520cf8Sgluk 		nsect = nerr / 5 + (nerr % 5 != 0 ? 1 : 0);
14935ae94ef8Sderaadt 		if ((newbuf = calloc(nsect, DEV_BSIZE)) == NULL)
14941ed98fdfSderaadt 			err(1, "calloc()");
149530520cf8Sgluk 		memset(&req, 0, sizeof(req));
149630520cf8Sgluk 		req.flags = ATACMD_READ;
149730520cf8Sgluk 		req.command = ATAPI_SMART;
149847f8c6e8Sgluk 		req.features = ATA_SMART_READLOG;
149930520cf8Sgluk 		req.sec_count = nsect;
150030520cf8Sgluk 		req.sec_num = SMART_READLOG_COMP;
150130520cf8Sgluk 		req.cylinder = 0xc24f;
150230520cf8Sgluk 		req.databuf = (caddr_t)newbuf;
150330520cf8Sgluk 		req.datalen = nsect * DEV_BSIZE;
150430520cf8Sgluk 		req.timeout = 1000;
150530520cf8Sgluk 		ata_command(&req);
150630520cf8Sgluk 
150730520cf8Sgluk 		n = 0;
150830520cf8Sgluk 		data = (struct smart_log_comp *)
150930520cf8Sgluk 		    (newbuf + (nsect - 1) * DEV_BSIZE);
151030520cf8Sgluk 		do {
151130520cf8Sgluk 			printf("Error %d:\n", n + 1);
151230520cf8Sgluk 			smart_print_errdata(&data->errdata[i-- % 5]);
151330520cf8Sgluk 			if (i == -1)
151430520cf8Sgluk 				i = 254;
151530520cf8Sgluk 			if (i % 5 == 4)
151630520cf8Sgluk 				data = (struct smart_log_comp *)
151730520cf8Sgluk 				    (newbuf + (i / 5) * DEV_BSIZE);
151830520cf8Sgluk 		} while (++n < nerr);
151930520cf8Sgluk 	} else if (strcmp(argv[1], "selftest") == 0) {
152030520cf8Sgluk 		struct smart_log_self *data = (struct smart_log_self *)inbuf;
152130520cf8Sgluk 		int i, n;
152230520cf8Sgluk 
1523dc223e5cSgrange 		if (smart_cksum(inbuf, sizeof(inbuf)) != 0)
1524dc223e5cSgrange 			errx(1, "Checksum mismatch");
152530520cf8Sgluk 
152630520cf8Sgluk 		if (data->index == 0) {
152730520cf8Sgluk 			printf("No log entries\n");
152830520cf8Sgluk 			return;
152930520cf8Sgluk 		}
153030520cf8Sgluk 
153130520cf8Sgluk 		/* circular buffer of 21 entries */
153230520cf8Sgluk 		i = data->index - 1;
153330520cf8Sgluk 		n = 0;
153430520cf8Sgluk 		do {
153530520cf8Sgluk 			/* don't print empty entries */
153630520cf8Sgluk 			if ((data->desc[i].time1 | data->desc[i].time2) == 0)
153730520cf8Sgluk 				break;
153830520cf8Sgluk 			printf("Test %d\n", n + 1);
153930520cf8Sgluk 			printf("    LBA Low: 0x%x\n", data->desc[i].reg_lbalo);
154030520cf8Sgluk 			printf("    status: %s\n",
154130520cf8Sgluk 			    valtostr(SMART_SELFSTAT_STAT(
154230520cf8Sgluk 			    data->desc[i].selfstat),
1543be0cee2fSsemarie 			    smart_selfstat, "?"));
154430520cf8Sgluk 			printf("    timestamp: %d\n",
154530520cf8Sgluk 			    MAKEWORD(data->desc[i].time1,
154630520cf8Sgluk 				     data->desc[i].time2));
154730520cf8Sgluk 			printf("    failure checkpoint byte: 0x%x\n",
154830520cf8Sgluk 			    data->desc[i].chkpnt);
154930520cf8Sgluk 			printf("    failing LBA: 0x%x\n",
155030520cf8Sgluk 			    MAKEDWORD(data->desc[i].lbafail1,
155130520cf8Sgluk 				      data->desc[i].lbafail2,
155230520cf8Sgluk 				      data->desc[i].lbafail3,
155330520cf8Sgluk 				      data->desc[i].lbafail4));
155430520cf8Sgluk 			if (--i == -1)
155530520cf8Sgluk 				i = 20;
155630520cf8Sgluk 		} while (++n < 21);
155730520cf8Sgluk 	}
155830520cf8Sgluk 
155930520cf8Sgluk 	return;
156030520cf8Sgluk usage:
1561d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s log\n", __progname, argv[0]);
156230520cf8Sgluk 	exit(1);
156330520cf8Sgluk }
156430520cf8Sgluk 
156530520cf8Sgluk #define SMART_PRINTREG(str, reg)				\
156630520cf8Sgluk 	printf(str "0x%02x\t0x%02x\t0x%02x\t0x%02x\t0x%02x\n",	\
156730520cf8Sgluk 	    data->cmd[0].reg,					\
156830520cf8Sgluk 	    data->cmd[1].reg,					\
156930520cf8Sgluk 	    data->cmd[2].reg,					\
157030520cf8Sgluk 	    data->cmd[3].reg,					\
157130520cf8Sgluk 	    data->cmd[4].reg)
157230520cf8Sgluk 
157330520cf8Sgluk void
smart_print_errdata(struct smart_log_errdata * data)1574bc52e260Sderaadt smart_print_errdata(struct smart_log_errdata *data)
157530520cf8Sgluk {
157630520cf8Sgluk 	printf("    error register: 0x%x\n", data->err.reg_err);
157730520cf8Sgluk 	printf("    sector count register: 0x%x\n", data->err.reg_seccnt);
157830520cf8Sgluk 	printf("    LBA Low register: 0x%x\n", data->err.reg_lbalo);
157930520cf8Sgluk 	printf("    LBA Mid register: 0x%x\n", data->err.reg_lbamid);
158030520cf8Sgluk 	printf("    LBA High register: 0x%x\n", data->err.reg_lbahi);
158130520cf8Sgluk 	printf("    device register: 0x%x\n", data->err.reg_dev);
158230520cf8Sgluk 	printf("    status register: 0x%x\n", data->err.reg_stat);
1583be0cee2fSsemarie 	printf("    state: %s\n", valtostr(data->err.state, smart_logstat, "?"));
158430520cf8Sgluk 	printf("    timestamp: %d\n", MAKEWORD(data->err.time1,
158530520cf8Sgluk 					       data->err.time2));
158630520cf8Sgluk 	printf("    history:\n");
158730520cf8Sgluk 	SMART_PRINTREG("\tcontrol register:\t", reg_ctl);
158830520cf8Sgluk 	SMART_PRINTREG("\tfeatures register:\t", reg_feat);
158930520cf8Sgluk 	SMART_PRINTREG("\tsector count register:\t", reg_seccnt);
159030520cf8Sgluk 	SMART_PRINTREG("\tLBA Low register:\t", reg_lbalo);
159130520cf8Sgluk 	SMART_PRINTREG("\tLBA Mid register:\t", reg_lbamid);
159230520cf8Sgluk 	SMART_PRINTREG("\tLBA High register:\t", reg_lbahi);
159330520cf8Sgluk 	SMART_PRINTREG("\tdevice register:\t", reg_dev);
159430520cf8Sgluk 	SMART_PRINTREG("\tcommand register:\t", reg_cmd);
159530520cf8Sgluk 	printf("\ttimestamp:\t\t"
159630520cf8Sgluk 	    "%d\t%d\t%d\t%d\t%d\n",
159730520cf8Sgluk 	    MAKEDWORD(data->cmd[0].time1, data->cmd[0].time2,
159830520cf8Sgluk 		      data->cmd[0].time3, data->cmd[0].time4),
159930520cf8Sgluk 	    MAKEDWORD(data->cmd[1].time1, data->cmd[1].time2,
160030520cf8Sgluk 		      data->cmd[1].time3, data->cmd[1].time4),
160130520cf8Sgluk 	    MAKEDWORD(data->cmd[2].time1, data->cmd[2].time2,
160230520cf8Sgluk 		      data->cmd[2].time3, data->cmd[2].time4),
160330520cf8Sgluk 	    MAKEDWORD(data->cmd[3].time1, data->cmd[3].time2,
160430520cf8Sgluk 		      data->cmd[3].time3, data->cmd[3].time4),
160530520cf8Sgluk 	    MAKEDWORD(data->cmd[4].time1, data->cmd[4].time2,
160630520cf8Sgluk 		      data->cmd[4].time3, data->cmd[4].time4));
160730520cf8Sgluk }
160830520cf8Sgluk 
160930520cf8Sgluk int
smart_cksum(u_int8_t * data,size_t len)16104bf7812dSmoritz smart_cksum(u_int8_t *data, size_t len)
161130520cf8Sgluk {
161230520cf8Sgluk 	u_int8_t sum = 0;
16134bf7812dSmoritz 	size_t i;
161430520cf8Sgluk 
161530520cf8Sgluk 	for (i = 0; i < len; i++)
161630520cf8Sgluk 		sum += data[i];
161730520cf8Sgluk 
1618dc223e5cSgrange 	return (sum);
161930520cf8Sgluk }
162030520cf8Sgluk 
162130520cf8Sgluk /*
162230520cf8Sgluk  * Read device attributes
162330520cf8Sgluk  */
162430520cf8Sgluk void
device_attr(int argc,char * argv[])1625bc52e260Sderaadt device_attr(int argc, char *argv[])
162630520cf8Sgluk {
162730520cf8Sgluk 	struct atareq req;
162830520cf8Sgluk 	struct smart_read attr_val;
162930520cf8Sgluk 	struct smart_threshold attr_thr;
163030520cf8Sgluk 	struct attribute *attr;
163130520cf8Sgluk 	struct threshold *thr;
163230520cf8Sgluk 	const char *attr_name;
163330520cf8Sgluk 	static const char hex[]="0123456789abcdef";
163430520cf8Sgluk 	char raw[13], *format;
163530520cf8Sgluk 	int i, k, threshold_exceeded = 0;
163630520cf8Sgluk 
1637d36724d1Sgluk 	if (argc != 1)
1638d36724d1Sgluk 		goto usage;
1639d36724d1Sgluk 
164030520cf8Sgluk 	memset(&req, 0, sizeof(req));
164130520cf8Sgluk 	memset(&attr_val, 0, sizeof(attr_val));	/* XXX */
164230520cf8Sgluk 	memset(&attr_thr, 0, sizeof(attr_thr));	/* XXX */
164330520cf8Sgluk 
164430520cf8Sgluk 	req.command = ATAPI_SMART;
164530520cf8Sgluk 	req.cylinder = 0xc24f;		/* LBA High = C2h, LBA Mid = 4Fh */
164630520cf8Sgluk 	req.timeout = 1000;
164730520cf8Sgluk 
164847f8c6e8Sgluk 	req.features = ATA_SMART_READ;
164930520cf8Sgluk 	req.flags = ATACMD_READ;
165030520cf8Sgluk 	req.databuf = (caddr_t)&attr_val;
165130520cf8Sgluk 	req.datalen = sizeof(attr_val);
165230520cf8Sgluk 	ata_command(&req);
165330520cf8Sgluk 
165447f8c6e8Sgluk 	req.features = ATA_SMART_THRESHOLD;
165530520cf8Sgluk 	req.flags = ATACMD_READ;
165630520cf8Sgluk 	req.databuf = (caddr_t)&attr_thr;
165730520cf8Sgluk 	req.datalen = sizeof(attr_thr);
165830520cf8Sgluk 	ata_command(&req);
165930520cf8Sgluk 
1660*5095123fSyasuoka 	if (smart_cksum((u_int8_t *)&attr_val, sizeof(attr_val)) != 0)
1661*5095123fSyasuoka 		errx(1, "Checksum mismatch (attr_val)");
1662*5095123fSyasuoka 
1663*5095123fSyasuoka 	if (smart_cksum((u_int8_t *)&attr_thr, sizeof(attr_thr)) != 0)
1664*5095123fSyasuoka 		errx(1, "Checksum mismatch (attr_thr)");
166530520cf8Sgluk 
166630520cf8Sgluk 	attr = attr_val.attribute;
166730520cf8Sgluk 	thr = attr_thr.threshold;
1668d36724d1Sgluk 
1669d36724d1Sgluk 	printf("Attributes table revision: %d\n", attr_val.revision);
167030520cf8Sgluk 	printf("ID\tAttribute name\t\t\tThreshold\tValue\tRaw\n");
167130520cf8Sgluk 	for (i = 0; i < 30; i++) {
167230520cf8Sgluk 		if (thr[i].id != 0 && thr[i].id == attr[i].id) {
1673be0cee2fSsemarie 			attr_name = valtostr(thr[i].id, ibm_attr_names,
1674be0cee2fSsemarie 			    "Unknown");
167530520cf8Sgluk 
167630520cf8Sgluk 			for (k = 0; k < 6; k++) {
167730520cf8Sgluk 				u_int8_t b;
167830520cf8Sgluk 				b = attr[i].raw[6 - k];
167930520cf8Sgluk 				raw[k + k] = hex[b >> 4];
168030520cf8Sgluk 				raw[k + k + 1] = hex[b & 0x0f];
168130520cf8Sgluk 			}
168230520cf8Sgluk 			raw[k + k] = '\0';
168330520cf8Sgluk 			if (thr[i].value >= attr[i].value) {
168430520cf8Sgluk 				++threshold_exceeded;
168530520cf8Sgluk 				format = "%3d    *%-32.32s %3d\t\t%3d\t0x%s\n";
168630520cf8Sgluk 			} else {
168730520cf8Sgluk 				format = "%3d\t%-32.32s %3d\t\t%3d\t0x%s\n";
168830520cf8Sgluk 			}
168930520cf8Sgluk 			printf(format, thr[i].id, attr_name,
169030520cf8Sgluk 			    thr[i].value, attr[i].value, raw);
169130520cf8Sgluk 		}
169230520cf8Sgluk 	}
169330520cf8Sgluk 	if (threshold_exceeded)
169430520cf8Sgluk 		fprintf(stderr, "One or more threshold values exceeded!\n");
1695d36724d1Sgluk 
1696d36724d1Sgluk 	return;
1697d36724d1Sgluk 
1698d36724d1Sgluk usage:
1699d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]);
1700d36724d1Sgluk 	exit(1);
170130520cf8Sgluk }
170230520cf8Sgluk 
170330520cf8Sgluk /*
170430520cf8Sgluk  * Set the automatic acoustic management on the disk.
17053ded539eSderaadt  */
17063ded539eSderaadt void
device_acoustic(int argc,char * argv[])1707bc52e260Sderaadt device_acoustic(int argc, char *argv[])
17083ded539eSderaadt {
17094bf7812dSmoritz 	u_char acoustic;
17103ded539eSderaadt 	struct atareq req;
17114bf7812dSmoritz 	const char *errstr;
17123ded539eSderaadt 
171330520cf8Sgluk 	if (argc != 2)
17143ded539eSderaadt 		goto usage;
17153ded539eSderaadt 
17164bf7812dSmoritz 	acoustic = strtonum(argv[1], 0, 126, &errstr);
17174bf7812dSmoritz 	if (errstr)
17184bf7812dSmoritz 		errx(1, "Acoustic management value \"%s\" is %s "
17194bf7812dSmoritz 		    "(valid values: 0 - 126)", argv[1], errstr);
17203ded539eSderaadt 
17213ded539eSderaadt 	memset(&req, 0, sizeof(req));
17223ded539eSderaadt 
17233ded539eSderaadt 	req.sec_count = acoustic + 0x80;
17243ded539eSderaadt 
17253ded539eSderaadt 	req.command = SET_FEATURES ;
17263ded539eSderaadt 	req.features = WDSF_AAM_EN ;
17273ded539eSderaadt 	req.timeout = 1000;
17283ded539eSderaadt 
17293ded539eSderaadt 	ata_command(&req);
17303ded539eSderaadt 
17313ded539eSderaadt 	return;
17323ded539eSderaadt 
17333ded539eSderaadt usage:
1734d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s acoustic-management-level\n",
173530520cf8Sgluk 	    __progname, argv[0]);
17363ded539eSderaadt 	exit(1);
17373ded539eSderaadt }
17383ded539eSderaadt 
17393ded539eSderaadt /*
17403ded539eSderaadt  * Set the advanced power managmement on the disk. Power management
17413ded539eSderaadt  * levels are translated from user-range 0-253 to ATAPI levels 1-0xFD
17423ded539eSderaadt  * to keep a uniform interface to the user.
17433ded539eSderaadt  */
17443ded539eSderaadt void
device_apm(int argc,char * argv[])1745bc52e260Sderaadt device_apm(int argc, char *argv[])
17463ded539eSderaadt {
17474bf7812dSmoritz 	u_char power;
17483ded539eSderaadt 	struct atareq req;
17494bf7812dSmoritz 	const char *errstr;
17503ded539eSderaadt 
175130520cf8Sgluk 	if (argc != 2)
17523ded539eSderaadt 		goto usage;
17533ded539eSderaadt 
17544bf7812dSmoritz 	power = strtonum(argv[1], 0, 253, &errstr);
17554bf7812dSmoritz 	if (errstr)
17564bf7812dSmoritz 		errx(1, "Advanced power management value \"%s\" is %s "
17574bf7812dSmoritz 		    "(valid values: 0 - 253)", argv[1], errstr);
17583ded539eSderaadt 
17593ded539eSderaadt 	memset(&req, 0, sizeof(req));
17603ded539eSderaadt 
17613ded539eSderaadt 	req.sec_count = power + 0x01;
17623ded539eSderaadt 
17633ded539eSderaadt 	req.command = SET_FEATURES ;
17643ded539eSderaadt 	req.features = WDSF_APM_EN ;
17653ded539eSderaadt 	req.timeout = 1000;
17663ded539eSderaadt 
17673ded539eSderaadt 	ata_command(&req);
17683ded539eSderaadt 
17693ded539eSderaadt 	return;
17703ded539eSderaadt 
17713ded539eSderaadt usage:
1772d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s power-management-level\n",
177330520cf8Sgluk 	    __progname, argv[0]);
17743ded539eSderaadt 	exit(1);
17753ded539eSderaadt }
17763ded539eSderaadt 
17773ded539eSderaadt /*
17783ded539eSderaadt  * En/disable features (the automatic acoustic managmement, Advanced Power
17793ded539eSderaadt  * Management) on the disk.
17803ded539eSderaadt  */
17813ded539eSderaadt void
device_feature(int argc,char * argv[])1782bc52e260Sderaadt device_feature(int argc, char *argv[])
17833ded539eSderaadt {
17843ded539eSderaadt 	struct atareq req;
17853ded539eSderaadt 
178630520cf8Sgluk 	if (argc != 1)
17873ded539eSderaadt 		goto usage;
17883ded539eSderaadt 
17893ded539eSderaadt 	memset(&req, 0, sizeof(req));
17903ded539eSderaadt 
17913ded539eSderaadt 	req.command = SET_FEATURES ;
17923ded539eSderaadt 
179330520cf8Sgluk 	if (strcmp(argv[0], "acousticdisable") == 0)
17943ded539eSderaadt 		req.features = WDSF_AAM_DS;
179530520cf8Sgluk 	else if (strcmp(argv[0], "readaheadenable") == 0)
17963ded539eSderaadt 		req.features = WDSF_READAHEAD_EN;
179730520cf8Sgluk 	else if (strcmp(argv[0], "readaheaddisable") == 0)
17983ded539eSderaadt 		req.features = WDSF_READAHEAD_DS;
179930520cf8Sgluk 	else if (strcmp(argv[0], "writecacheenable") == 0)
18003ded539eSderaadt 		req.features = WDSF_EN_WR_CACHE;
180130520cf8Sgluk 	else if (strcmp(argv[0], "writecachedisable") == 0)
18023ded539eSderaadt 		req.features = WDSF_WRITE_CACHE_DS;
180330520cf8Sgluk 	else if (strcmp(argv[0], "apmdisable") == 0)
18043ded539eSderaadt 		req.features = WDSF_APM_DS;
1805fb6e6f74Sgrange 	else if (strcmp(argv[0], "podenable") == 0)
1806fb6e6f74Sgrange 		req.features = WDSF_POD_EN;
1807fb6e6f74Sgrange 	else if (strcmp(argv[0], "poddisable") == 0)
1808fb6e6f74Sgrange 		req.features = WDSF_POD_DS;
180930520cf8Sgluk 	else if (strcmp(argv[0], "puisenable") == 0)
18103ded539eSderaadt 		req.features = WDSF_PUIS_EN;
181130520cf8Sgluk 	else if (strcmp(argv[0], "puisdisable") == 0)
18123ded539eSderaadt 		req.features = WDSF_PUIS_DS;
181330520cf8Sgluk 	else if (strcmp(argv[0], "puisspinup") == 0)
18143ded539eSderaadt 		req.features = WDSF_PUIS_SPINUP;
18153ded539eSderaadt 	else
18163ded539eSderaadt 		goto usage;
18173ded539eSderaadt 
18183ded539eSderaadt 	req.timeout = 1000;
18193ded539eSderaadt 
18203ded539eSderaadt 	ata_command(&req);
18213ded539eSderaadt 
18223ded539eSderaadt 	return;
18233ded539eSderaadt 
18243ded539eSderaadt usage:
1825d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]);
18263ded539eSderaadt 	exit(1);
18273ded539eSderaadt }
18283ded539eSderaadt 
18293ded539eSderaadt /*
1830239cdf63Scsapuntz  * Set the idle timer on the disk.  Set it for either idle mode or
1831239cdf63Scsapuntz  * standby mode, depending on how we were invoked.
1832239cdf63Scsapuntz  */
1833239cdf63Scsapuntz void
device_setidle(int argc,char * argv[])1834bc52e260Sderaadt device_setidle(int argc, char *argv[])
1835239cdf63Scsapuntz {
1836239cdf63Scsapuntz 	unsigned long idle;
1837239cdf63Scsapuntz 	struct atareq req;
1838239cdf63Scsapuntz 	char *end;
1839239cdf63Scsapuntz 
184030520cf8Sgluk 	if (argc != 2)
1841239cdf63Scsapuntz 		goto usage;
1842239cdf63Scsapuntz 
184330520cf8Sgluk 	idle = strtoul(argv[1], &end, 0);
1844239cdf63Scsapuntz 
1845dc223e5cSgrange 	if (*end != '\0' || idle > 19800)
1846dc223e5cSgrange 		errx(1, "Invalid idle time: \"%s\" "
18474bf7812dSmoritz 		    "(valid values: 1 - 19800)", argv[1]);
1848239cdf63Scsapuntz 
1849dc223e5cSgrange 	if (idle != 0 && idle < 5)
1850dc223e5cSgrange 		errx(1, "Idle timer must be at least 5 seconds");
1851239cdf63Scsapuntz 
1852239cdf63Scsapuntz 	memset(&req, 0, sizeof(req));
1853239cdf63Scsapuntz 
1854239cdf63Scsapuntz 	if (idle <= 240 * 5)
1855239cdf63Scsapuntz 		req.sec_count = idle / 5;
1856239cdf63Scsapuntz 	else
1857239cdf63Scsapuntz 		req.sec_count = idle / (30 * 60) + 240;
1858239cdf63Scsapuntz 
185930520cf8Sgluk 	if (strcmp(argv[0], "setstandby") == 0)
186030520cf8Sgluk 		req.command = WDCC_STANDBY;
186130520cf8Sgluk 	else if (strcmp(argv[0], "setidle") == 0)
186230520cf8Sgluk 		req.command = WDCC_IDLE;
186330520cf8Sgluk 	else
186430520cf8Sgluk 		goto usage;
1865239cdf63Scsapuntz 	req.timeout = 1000;
1866239cdf63Scsapuntz 
1867239cdf63Scsapuntz 	ata_command(&req);
1868239cdf63Scsapuntz 
1869239cdf63Scsapuntz 	return;
1870239cdf63Scsapuntz 
1871239cdf63Scsapuntz usage:
1872d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s %s\n", __progname, argv[0],
1873d3fa79dbSsobrado 	    (strcmp(argv[0], "setidle") == 0) ? "idle-timer" : "standby-timer");
1874239cdf63Scsapuntz 	exit(1);
1875239cdf63Scsapuntz }
1876239cdf63Scsapuntz 
1877239cdf63Scsapuntz /*
1878239cdf63Scsapuntz  * Query the device for the current power mode
1879239cdf63Scsapuntz  */
1880239cdf63Scsapuntz void
device_checkpower(int argc,char * argv[])1881bc52e260Sderaadt device_checkpower(int argc, char *argv[])
1882239cdf63Scsapuntz {
1883239cdf63Scsapuntz 	struct atareq req;
1884239cdf63Scsapuntz 
188530520cf8Sgluk 	if (argc != 1)
1886239cdf63Scsapuntz 		goto usage;
1887239cdf63Scsapuntz 
1888239cdf63Scsapuntz 	memset(&req, 0, sizeof(req));
1889239cdf63Scsapuntz 
1890239cdf63Scsapuntz 	req.command = WDCC_CHECK_PWR;
1891239cdf63Scsapuntz 	req.timeout = 1000;
1892239cdf63Scsapuntz 	req.flags = ATACMD_READREG;
1893239cdf63Scsapuntz 
1894239cdf63Scsapuntz 	ata_command(&req);
1895239cdf63Scsapuntz 
1896239cdf63Scsapuntz 	printf("Current power status: ");
1897239cdf63Scsapuntz 
1898239cdf63Scsapuntz 	switch (req.sec_count) {
1899239cdf63Scsapuntz 	case 0x00:
1900239cdf63Scsapuntz 		printf("Standby mode\n");
1901239cdf63Scsapuntz 		break;
1902239cdf63Scsapuntz 	case 0x80:
1903239cdf63Scsapuntz 		printf("Idle mode\n");
1904239cdf63Scsapuntz 		break;
1905239cdf63Scsapuntz 	case 0xff:
1906239cdf63Scsapuntz 		printf("Active mode\n");
1907239cdf63Scsapuntz 		break;
1908239cdf63Scsapuntz 	default:
1909239cdf63Scsapuntz 		printf("Unknown power code (%02x)\n", req.sec_count);
1910239cdf63Scsapuntz 	}
1911239cdf63Scsapuntz 
1912239cdf63Scsapuntz 	return;
1913239cdf63Scsapuntz usage:
1914d3fa79dbSsobrado 	fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]);
1915239cdf63Scsapuntz 	exit(1);
1916239cdf63Scsapuntz }
1917