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