1 |
|
|
/* $OpenBSD: atactl.c,v 1.46 2015/08/20 22:02:20 deraadt Exp $ */ |
2 |
|
|
/* $NetBSD: atactl.c,v 1.4 1999/02/24 18:49:14 jwise Exp $ */ |
3 |
|
|
|
4 |
|
|
/*- |
5 |
|
|
* Copyright (c) 1998 The NetBSD Foundation, Inc. |
6 |
|
|
* All rights reserved. |
7 |
|
|
* |
8 |
|
|
* This code is derived from software contributed to The NetBSD Foundation |
9 |
|
|
* by Ken Hornstein. |
10 |
|
|
* |
11 |
|
|
* Redistribution and use in source and binary forms, with or without |
12 |
|
|
* modification, are permitted provided that the following conditions |
13 |
|
|
* are met: |
14 |
|
|
* 1. Redistributions of source code must retain the above copyright |
15 |
|
|
* notice, this list of conditions and the following disclaimer. |
16 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
17 |
|
|
* notice, this list of conditions and the following disclaimer in the |
18 |
|
|
* documentation and/or other materials provided with the distribution. |
19 |
|
|
* |
20 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
21 |
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
22 |
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
23 |
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
24 |
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
25 |
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
26 |
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
27 |
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 |
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
29 |
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
30 |
|
|
* POSSIBILITY OF SUCH DAMAGE. |
31 |
|
|
*/ |
32 |
|
|
|
33 |
|
|
/* |
34 |
|
|
* atactl(8) - a program to control ATA devices. |
35 |
|
|
*/ |
36 |
|
|
|
37 |
|
|
#include <sys/param.h> /* DEV_BSIZE */ |
38 |
|
|
#include <sys/ioctl.h> |
39 |
|
|
|
40 |
|
|
#include <err.h> |
41 |
|
|
#include <errno.h> |
42 |
|
|
#include <fcntl.h> |
43 |
|
|
#include <stdio.h> |
44 |
|
|
#include <stdlib.h> |
45 |
|
|
#include <string.h> |
46 |
|
|
#include <unistd.h> |
47 |
|
|
#include <util.h> |
48 |
|
|
|
49 |
|
|
#include <dev/ata/atareg.h> |
50 |
|
|
#include <dev/ic/wdcreg.h> |
51 |
|
|
#include <dev/ic/wdcevent.h> |
52 |
|
|
#include <sys/ataio.h> |
53 |
|
|
|
54 |
|
|
#include "atasec.h" |
55 |
|
|
#include "atasmart.h" |
56 |
|
|
|
57 |
|
|
struct command { |
58 |
|
|
const char *cmd_name; |
59 |
|
|
void (*cmd_func)(int, char *[]); |
60 |
|
|
}; |
61 |
|
|
|
62 |
|
|
struct bitinfo { |
63 |
|
|
u_int bitmask; |
64 |
|
|
const char *string; |
65 |
|
|
}; |
66 |
|
|
|
67 |
|
|
struct valinfo { |
68 |
|
|
int value; |
69 |
|
|
const char *string; |
70 |
|
|
}; |
71 |
|
|
|
72 |
|
|
int main(int, char *[]); |
73 |
|
|
__dead void usage(void); |
74 |
|
|
void ata_command(struct atareq *); |
75 |
|
|
void print_bitinfo(const char *, u_int, struct bitinfo *); |
76 |
|
|
int strtoval(const char *, struct valinfo *); |
77 |
|
|
const char *valtostr(int, struct valinfo *); |
78 |
|
|
|
79 |
|
|
int fd; /* file descriptor for device */ |
80 |
|
|
|
81 |
|
|
extern char *__progname; /* from crt0.o */ |
82 |
|
|
|
83 |
|
|
void device_dump(int, char*[]); |
84 |
|
|
void device_identify(int, char *[]); |
85 |
|
|
void device_setidle(int, char *[]); |
86 |
|
|
void device_idle(int, char *[]); |
87 |
|
|
void device_checkpower(int, char *[]); |
88 |
|
|
void device_acoustic(int, char *[]); |
89 |
|
|
void device_apm(int, char *[]); |
90 |
|
|
void device_feature(int, char *[]); |
91 |
|
|
void device_sec_setpass(int, char *[]); |
92 |
|
|
void device_sec_unlock(int, char *[]); |
93 |
|
|
void device_sec_erase(int, char *[]); |
94 |
|
|
void device_sec_freeze(int, char *[]); |
95 |
|
|
void device_sec_disablepass(int, char *[]); |
96 |
|
|
void device_smart_enable(int, char *[]); |
97 |
|
|
void device_smart_disable(int, char *[]); |
98 |
|
|
void device_smart_status(int, char *[]); |
99 |
|
|
void device_smart_autosave(int, char *[]); |
100 |
|
|
void device_smart_offline(int, char *[]); |
101 |
|
|
void device_smart_read(int, char *[]); |
102 |
|
|
void device_smart_readlog(int, char *[]); |
103 |
|
|
void device_attr(int, char *[]); |
104 |
|
|
|
105 |
|
|
void smart_print_errdata(struct smart_log_errdata *); |
106 |
|
|
int smart_cksum(u_int8_t *, size_t); |
107 |
|
|
|
108 |
|
|
char *sec_getpass(int, int); |
109 |
|
|
|
110 |
|
|
struct command commands[] = { |
111 |
|
|
{ "dump", device_dump }, |
112 |
|
|
{ "identify", device_identify }, |
113 |
|
|
{ "setidle", device_setidle }, |
114 |
|
|
{ "setstandby", device_setidle }, |
115 |
|
|
{ "idle", device_idle }, |
116 |
|
|
{ "standby", device_idle }, |
117 |
|
|
{ "sleep", device_idle }, |
118 |
|
|
{ "checkpower", device_checkpower }, |
119 |
|
|
{ "acousticdisable", device_feature }, |
120 |
|
|
{ "acousticset", device_acoustic }, |
121 |
|
|
{ "apmdisable", device_feature }, |
122 |
|
|
{ "apmset", device_apm }, |
123 |
|
|
{ "poddisable", device_feature }, |
124 |
|
|
{ "podenable", device_feature }, |
125 |
|
|
{ "puisdisable", device_feature }, |
126 |
|
|
{ "puisenable", device_feature }, |
127 |
|
|
{ "puisspinup", device_feature }, |
128 |
|
|
{ "readaheaddisable", device_feature }, |
129 |
|
|
{ "readaheadenable", device_feature }, |
130 |
|
|
{ "secsetpass", device_sec_setpass }, |
131 |
|
|
{ "secunlock", device_sec_unlock }, |
132 |
|
|
{ "secerase", device_sec_erase }, |
133 |
|
|
{ "secfreeze", device_sec_freeze }, |
134 |
|
|
{ "secdisablepass", device_sec_disablepass }, |
135 |
|
|
{ "smartenable", device_smart_enable }, |
136 |
|
|
{ "smartdisable", device_smart_disable }, |
137 |
|
|
{ "smartstatus", device_smart_status }, |
138 |
|
|
{ "smartautosave", device_smart_autosave }, |
139 |
|
|
{ "smartoffline", device_smart_offline }, |
140 |
|
|
{ "smartread", device_smart_read }, |
141 |
|
|
{ "smartreadlog", device_smart_readlog }, |
142 |
|
|
{ "readattr", device_attr }, |
143 |
|
|
{ "writecachedisable", device_feature }, |
144 |
|
|
{ "writecacheenable", device_feature }, |
145 |
|
|
{ NULL, NULL }, |
146 |
|
|
}; |
147 |
|
|
|
148 |
|
|
/* |
149 |
|
|
* Tables containing bitmasks used for error reporting and |
150 |
|
|
* device identification. |
151 |
|
|
*/ |
152 |
|
|
|
153 |
|
|
struct bitinfo ata_caps[] = { |
154 |
|
|
{ ATA_CAP_STBY, "ATA standby timer values" }, |
155 |
|
|
{ WDC_CAP_IORDY, "IORDY operation" }, |
156 |
|
|
{ WDC_CAP_IORDY_DSBL, "IORDY disabling" }, |
157 |
|
|
{ 0, NULL }, |
158 |
|
|
}; |
159 |
|
|
|
160 |
|
|
struct bitinfo ata_vers[] = { |
161 |
|
|
{ WDC_VER_ATA1, "ATA-1" }, |
162 |
|
|
{ WDC_VER_ATA2, "ATA-2" }, |
163 |
|
|
{ WDC_VER_ATA3, "ATA-3" }, |
164 |
|
|
{ WDC_VER_ATA4, "ATA-4" }, |
165 |
|
|
{ WDC_VER_ATA5, "ATA-5" }, |
166 |
|
|
{ WDC_VER_ATA6, "ATA-6" }, |
167 |
|
|
{ WDC_VER_ATA7, "ATA-7" }, |
168 |
|
|
{ WDC_VER_ATA8, "ATA-8" }, |
169 |
|
|
{ WDC_VER_ATA9, "ATA-9" }, |
170 |
|
|
{ WDC_VER_ATA10, "ATA-10" }, |
171 |
|
|
{ WDC_VER_ATA11, "ATA-11" }, |
172 |
|
|
{ WDC_VER_ATA12, "ATA-12" }, |
173 |
|
|
{ WDC_VER_ATA13, "ATA-13" }, |
174 |
|
|
{ WDC_VER_ATA14, "ATA-14" }, |
175 |
|
|
{ 0, NULL }, |
176 |
|
|
}; |
177 |
|
|
|
178 |
|
|
struct bitinfo ata_cmd_set1[] = { |
179 |
|
|
{ WDC_CMD1_NOP, "NOP command" }, |
180 |
|
|
{ WDC_CMD1_RB, "READ BUFFER command" }, |
181 |
|
|
{ WDC_CMD1_WB, "WRITE BUFFER command" }, |
182 |
|
|
{ WDC_CMD1_HPA, "Host Protected Area feature set" }, |
183 |
|
|
{ WDC_CMD1_DVRST, "DEVICE RESET command" }, |
184 |
|
|
{ WDC_CMD1_SRV, "SERVICE interrupt" }, |
185 |
|
|
{ WDC_CMD1_RLSE, "Release interrupt" }, |
186 |
|
|
{ WDC_CMD1_AHEAD, "Read look-ahead" }, |
187 |
|
|
{ WDC_CMD1_CACHE, "Write cache" }, |
188 |
|
|
{ WDC_CMD1_PKT, "PACKET command feature set" }, |
189 |
|
|
{ WDC_CMD1_PM, "Power Management feature set" }, |
190 |
|
|
{ WDC_CMD1_REMOV, "Removable Media feature set" }, |
191 |
|
|
{ WDC_CMD1_SEC, "Security Mode feature set" }, |
192 |
|
|
{ WDC_CMD1_SMART, "SMART feature set" }, |
193 |
|
|
{ 0, NULL }, |
194 |
|
|
}; |
195 |
|
|
|
196 |
|
|
struct bitinfo ata_cmd_set2[] = { |
197 |
|
|
{ ATAPI_CMD2_FCE, "Flush Cache Ext command" }, |
198 |
|
|
{ ATAPI_CMD2_FC, "Flush Cache command" }, |
199 |
|
|
{ ATAPI_CMD2_DCO, "Device Configuration Overlay feature set" }, |
200 |
|
|
{ ATAPI_CMD2_48AD, "48bit address feature set" }, |
201 |
|
|
{ ATAPI_CMD2_AAM, "Automatic Acoustic Management feature set" }, |
202 |
|
|
{ ATAPI_CMD2_SM, "Set Max security extension commands" }, |
203 |
|
|
{ ATAPI_CMD2_SF, "Set Features subcommand required" }, |
204 |
|
|
{ ATAPI_CMD2_PUIS, "Power-up in standby feature set" }, |
205 |
|
|
{ WDC_CMD2_RMSN, "Removable Media Status Notification feature set" }, |
206 |
|
|
{ ATA_CMD2_APM, "Advanced Power Management feature set" }, |
207 |
|
|
{ ATA_CMD2_CFA, "CFA feature set" }, |
208 |
|
|
{ ATA_CMD2_RWQ, "READ/WRITE DMA QUEUED commands" }, |
209 |
|
|
{ WDC_CMD2_DM, "DOWNLOAD MICROCODE command" }, |
210 |
|
|
{ 0, NULL }, |
211 |
|
|
}; |
212 |
|
|
|
213 |
|
|
struct bitinfo ata_cmd_ext[] = { |
214 |
|
|
{ ATAPI_CMDE_IIUF, "IDLE IMMEDIATE with UNLOAD FEATURE" }, |
215 |
|
|
{ ATAPI_CMDE_MSER, "Media serial number" }, |
216 |
|
|
{ ATAPI_CMDE_TEST, "SMART self-test" }, |
217 |
|
|
{ ATAPI_CMDE_SLOG, "SMART error logging" }, |
218 |
|
|
{ 0, NULL }, |
219 |
|
|
}; |
220 |
|
|
|
221 |
|
|
/* |
222 |
|
|
* Tables containing bitmasks and values used for |
223 |
|
|
* SMART commands. |
224 |
|
|
*/ |
225 |
|
|
|
226 |
|
|
struct bitinfo smart_offcap[] = { |
227 |
|
|
{ SMART_OFFCAP_EXEC, "execute immediate" }, |
228 |
|
|
{ SMART_OFFCAP_ABORT, "abort/restart" }, |
229 |
|
|
{ SMART_OFFCAP_READSCAN, "read scanning" }, |
230 |
|
|
{ SMART_OFFCAP_SELFTEST, "self-test routines" }, |
231 |
|
|
{ 0, NULL} |
232 |
|
|
}; |
233 |
|
|
|
234 |
|
|
struct bitinfo smart_smartcap[] = { |
235 |
|
|
{ SMART_SMARTCAP_SAVE, "saving SMART data" }, |
236 |
|
|
{ SMART_SMARTCAP_AUTOSAVE, "enable/disable attribute autosave" }, |
237 |
|
|
{ 0, NULL } |
238 |
|
|
}; |
239 |
|
|
|
240 |
|
|
struct valinfo smart_autosave[] = { |
241 |
|
|
{ SMART_AUTOSAVE_EN, "enable" }, |
242 |
|
|
{ SMART_AUTOSAVE_DS, "disable" }, |
243 |
|
|
{ 0, NULL } |
244 |
|
|
}; |
245 |
|
|
|
246 |
|
|
struct valinfo smart_offline[] = { |
247 |
|
|
{ SMART_OFFLINE_COLLECT, "collect" }, |
248 |
|
|
{ SMART_OFFLINE_SHORTOFF, "shortoffline" }, |
249 |
|
|
{ SMART_OFFLINE_EXTENOFF, "extenoffline" }, |
250 |
|
|
{ SMART_OFFLINE_ABORT, "abort" }, |
251 |
|
|
{ SMART_OFFLINE_SHORTCAP, "shortcaptive" }, |
252 |
|
|
{ SMART_OFFLINE_EXTENCAP, "extencaptive" }, |
253 |
|
|
{ 0, NULL } |
254 |
|
|
}; |
255 |
|
|
|
256 |
|
|
struct valinfo smart_readlog[] = { |
257 |
|
|
{ SMART_READLOG_DIR, "directory" }, |
258 |
|
|
{ SMART_READLOG_SUM, "summary" }, |
259 |
|
|
{ SMART_READLOG_COMP, "comp" }, |
260 |
|
|
{ SMART_READLOG_SELF, "selftest" }, |
261 |
|
|
{ 0, NULL } |
262 |
|
|
}; |
263 |
|
|
|
264 |
|
|
struct valinfo smart_offstat[] = { |
265 |
|
|
{ SMART_OFFSTAT_NOTSTART, "never started" }, |
266 |
|
|
{ SMART_OFFSTAT_COMPLETE, "completed ok" }, |
267 |
|
|
{ SMART_OFFSTAT_SUSPEND, "suspended by an interrupting command" }, |
268 |
|
|
{ SMART_OFFSTAT_INTR, "aborted by an interrupting command" }, |
269 |
|
|
{ SMART_OFFSTAT_ERROR, "aborted due to fatal error" }, |
270 |
|
|
{ 0, NULL } |
271 |
|
|
}; |
272 |
|
|
|
273 |
|
|
struct valinfo smart_selfstat[] = { |
274 |
|
|
{ SMART_SELFSTAT_COMPLETE, "completed ok or not started" }, |
275 |
|
|
{ SMART_SELFSTAT_ABORT, "aborted" }, |
276 |
|
|
{ SMART_SELFSTAT_INTR, "hardware or software reset" }, |
277 |
|
|
{ SMART_SELFSTAT_ERROR, "fatal error" }, |
278 |
|
|
{ SMART_SELFSTAT_UNKFAIL, "unknown test element failed" }, |
279 |
|
|
{ SMART_SELFSTAT_ELFAIL, "electrical test element failed" }, |
280 |
|
|
{ SMART_SELFSTAT_SRVFAIL, "servo test element failed" }, |
281 |
|
|
{ SMART_SELFSTAT_RDFAIL, "read test element failed" }, |
282 |
|
|
{ 0, NULL } |
283 |
|
|
}; |
284 |
|
|
|
285 |
|
|
struct valinfo smart_logstat[] = { |
286 |
|
|
{ SMART_LOG_STATE_UNK, "unknown" }, |
287 |
|
|
{ SMART_LOG_STATE_SLEEP, "sleep" }, |
288 |
|
|
{ SMART_LOG_STATE_ACTIDL, "active/idle" }, |
289 |
|
|
{ SMART_LOG_STATE_OFFSELF, "off-line or self-test" }, |
290 |
|
|
{ 0, NULL } |
291 |
|
|
}; |
292 |
|
|
|
293 |
|
|
/* |
294 |
|
|
* Tables containing values used for reading |
295 |
|
|
* device attributes. |
296 |
|
|
*/ |
297 |
|
|
|
298 |
|
|
struct valinfo ibm_attr_names[] = { |
299 |
|
|
{ 1, "Raw Read Error Rate" }, |
300 |
|
|
{ 2, "Throughput Performance" }, |
301 |
|
|
{ 3, "Spin Up Time" }, |
302 |
|
|
{ 4, "Start/Stop Count" }, |
303 |
|
|
{ 5, "Reallocated Sector Count" }, |
304 |
|
|
{ 6, "Read Channel Margin" }, |
305 |
|
|
{ 7, "Seek Error Rate" }, |
306 |
|
|
{ 8, "Seek Time Performance" }, |
307 |
|
|
{ 9, "Power-On Hours Count" }, |
308 |
|
|
{ 10, "Spin Retry Count" }, |
309 |
|
|
{ 11, "Calibration Retry Count" }, |
310 |
|
|
{ 12, "Device Power Cycle Count" }, |
311 |
|
|
{ 13, "Soft Read Error Rate" }, |
312 |
|
|
{ 189, "High Fly Writes" }, |
313 |
|
|
{ 190, "Airflow Temperature" }, |
314 |
|
|
{ 191, "G-Sense Error Rate" }, |
315 |
|
|
{ 192, "Power-Off Retract Count" }, |
316 |
|
|
{ 193, "Load Cycle Count" }, |
317 |
|
|
{ 194, "Temperature" }, |
318 |
|
|
{ 195, "Hardware ECC Recovered" }, |
319 |
|
|
{ 196, "Reallocation Event Count" }, |
320 |
|
|
{ 197, "Current Pending Sector Count" }, |
321 |
|
|
{ 198, "Off-Line Scan Uncorrectable Sector Count" }, |
322 |
|
|
{ 199, "Ultra DMA CRC Error Count" }, |
323 |
|
|
{ 200, "Write Error Rate" }, |
324 |
|
|
{ 201, "Soft Read Error Rate" }, |
325 |
|
|
{ 202, "Data Address Mark Errors" }, |
326 |
|
|
{ 203, "Run Out Cancel" }, |
327 |
|
|
{ 204, "Soft ECC Correction" }, |
328 |
|
|
{ 205, "Thermal Asperity Check" }, |
329 |
|
|
{ 206, "Flying Height" }, |
330 |
|
|
{ 207, "Spin High Current" }, |
331 |
|
|
{ 208, "Spin Buzz" }, |
332 |
|
|
{ 209, "Offline Seek Performance" }, |
333 |
|
|
{ 220, "Disk Shift" }, |
334 |
|
|
{ 221, "G-Sense Error Rate" }, |
335 |
|
|
{ 222, "Loaded Hours" }, |
336 |
|
|
{ 223, "Load/Unload Retry Count" }, |
337 |
|
|
{ 224, "Load Friction" }, |
338 |
|
|
{ 225, "Load/Unload Cycle Count" }, |
339 |
|
|
{ 226, "Load-In Time" }, |
340 |
|
|
{ 227, "Torque Amplification Count" }, |
341 |
|
|
{ 228, "Power-Off Retract Count" }, |
342 |
|
|
{ 230, "GMR Head Amplitude" }, |
343 |
|
|
{ 231, "Temperature" }, |
344 |
|
|
{ 240, "Head Flying Hours" }, |
345 |
|
|
{ 250, "Read Error Retry Rate" }, |
346 |
|
|
{ 0, NULL }, |
347 |
|
|
}; |
348 |
|
|
|
349 |
|
|
#define MAKEWORD(b1, b2) \ |
350 |
|
|
(b2 << 8 | b1) |
351 |
|
|
#define MAKEDWORD(b1, b2, b3, b4) \ |
352 |
|
|
(b4 << 24 | b3 << 16 | b2 << 8 | b1) |
353 |
|
|
|
354 |
|
|
int |
355 |
|
|
main(int argc, char *argv[]) |
356 |
|
|
{ |
357 |
|
|
struct command *cmdp; |
358 |
|
|
|
359 |
|
|
if (argc < 2) |
360 |
|
|
usage(); |
361 |
|
|
|
362 |
|
|
/* |
363 |
|
|
* Open the device |
364 |
|
|
*/ |
365 |
|
|
if ((fd = opendev(argv[1], O_RDWR, OPENDEV_PART, NULL)) == -1) |
366 |
|
|
err(1, "%s", argv[1]); |
367 |
|
|
|
368 |
|
|
/* Skip program name and device name. */ |
369 |
|
|
if (argc != 2) { |
370 |
|
|
argv += 2; |
371 |
|
|
argc -= 2; |
372 |
|
|
} else { |
373 |
|
|
argv[1] = "identify"; |
374 |
|
|
argv += 1; |
375 |
|
|
argc -= 1; |
376 |
|
|
} |
377 |
|
|
|
378 |
|
|
/* Look up and call the command. */ |
379 |
|
|
for (cmdp = commands; cmdp->cmd_name != NULL; cmdp++) |
380 |
|
|
if (strcmp(argv[0], cmdp->cmd_name) == 0) |
381 |
|
|
break; |
382 |
|
|
if (cmdp->cmd_name == NULL) |
383 |
|
|
errx(1, "unknown command: %s", argv[0]); |
384 |
|
|
|
385 |
|
|
(cmdp->cmd_func)(argc, argv); |
386 |
|
|
|
387 |
|
|
return (0); |
388 |
|
|
} |
389 |
|
|
|
390 |
|
|
__dead void |
391 |
|
|
usage(void) |
392 |
|
|
{ |
393 |
|
|
|
394 |
|
|
fprintf(stderr, "usage: %s device [command [arg]]\n", __progname); |
395 |
|
|
exit(1); |
396 |
|
|
} |
397 |
|
|
|
398 |
|
|
/* |
399 |
|
|
* Wrapper that calls ATAIOCCOMMAND and checks for errors |
400 |
|
|
*/ |
401 |
|
|
void |
402 |
|
|
ata_command(struct atareq *req) |
403 |
|
|
{ |
404 |
|
|
if (ioctl(fd, ATAIOCCOMMAND, req) == -1) |
405 |
|
|
err(1, "ATAIOCCOMMAND failed"); |
406 |
|
|
|
407 |
|
|
switch (req->retsts) { |
408 |
|
|
|
409 |
|
|
case ATACMD_OK: |
410 |
|
|
return; |
411 |
|
|
case ATACMD_TIMEOUT: |
412 |
|
|
errx(1, "ATA command timed out"); |
413 |
|
|
case ATACMD_DF: |
414 |
|
|
errx(1, "ATA device returned a Device Fault"); |
415 |
|
|
case ATACMD_ERROR: |
416 |
|
|
if (req->error & WDCE_ABRT) |
417 |
|
|
errx(1, "ATA device returned Aborted Command"); |
418 |
|
|
else |
419 |
|
|
errx(1, "ATA device returned error register %0x", |
420 |
|
|
req->error); |
421 |
|
|
default: |
422 |
|
|
errx(1, "ATAIOCCOMMAND returned unknown result code %d", |
423 |
|
|
req->retsts); |
424 |
|
|
} |
425 |
|
|
} |
426 |
|
|
|
427 |
|
|
/* |
428 |
|
|
* Print out strings associated with particular bitmasks |
429 |
|
|
*/ |
430 |
|
|
void |
431 |
|
|
print_bitinfo(const char *f, u_int bits, struct bitinfo *binfo) |
432 |
|
|
{ |
433 |
|
|
|
434 |
|
|
for (; binfo->bitmask != 0; binfo++) |
435 |
|
|
if (bits & binfo->bitmask) |
436 |
|
|
printf(f, binfo->string); |
437 |
|
|
} |
438 |
|
|
|
439 |
|
|
/* |
440 |
|
|
* strtoval(): |
441 |
|
|
* returns value associated with given string, |
442 |
|
|
* if no value found -1 is returned. |
443 |
|
|
*/ |
444 |
|
|
int |
445 |
|
|
strtoval(const char *str, struct valinfo *vinfo) |
446 |
|
|
{ |
447 |
|
|
for (; vinfo->string != NULL; vinfo++) |
448 |
|
|
if (strcmp(str, vinfo->string) == 0) |
449 |
|
|
return (vinfo->value); |
450 |
|
|
return (-1); |
451 |
|
|
} |
452 |
|
|
|
453 |
|
|
/* |
454 |
|
|
* valtostr(): |
455 |
|
|
* returns string associated with given value, |
456 |
|
|
* if no string found NULL is returned. |
457 |
|
|
*/ |
458 |
|
|
const char * |
459 |
|
|
valtostr(int val, struct valinfo *vinfo) |
460 |
|
|
{ |
461 |
|
|
for (; vinfo->string != NULL; vinfo++) |
462 |
|
|
if (val == vinfo->value) |
463 |
|
|
return (vinfo->string); |
464 |
|
|
return (NULL); |
465 |
|
|
} |
466 |
|
|
|
467 |
|
|
/* |
468 |
|
|
* DEVICE COMMANDS |
469 |
|
|
*/ |
470 |
|
|
|
471 |
|
|
/* |
472 |
|
|
* device dump: |
473 |
|
|
* |
474 |
|
|
* extract issued ATA requests from the log buffer |
475 |
|
|
*/ |
476 |
|
|
void |
477 |
|
|
device_dump(int argc, char *argv[]) |
478 |
|
|
{ |
479 |
|
|
unsigned char buf[131072]; |
480 |
|
|
atagettrace_t agt; |
481 |
|
|
unsigned int total; |
482 |
|
|
unsigned int p = 0; |
483 |
|
|
int type; |
484 |
|
|
const char *types[] = { NULL, "status", "error", "ATAPI", |
485 |
|
|
"ATAPI done", "ATA cmd", "ATA", "select slave", |
486 |
|
|
"select master", "register read", "ATA LBA48" }; |
487 |
|
|
int num_types = sizeof(types) / sizeof(types[0]); |
488 |
|
|
int info; |
489 |
|
|
int entrysize; |
490 |
|
|
int i; |
491 |
|
|
int flags; |
492 |
|
|
|
493 |
|
|
if (argc != 1) |
494 |
|
|
goto usage; |
495 |
|
|
|
496 |
|
|
memset(&agt, 0, sizeof(agt)); |
497 |
|
|
agt.buf_size = sizeof(buf); |
498 |
|
|
agt.buf = buf; |
499 |
|
|
|
500 |
|
|
if (ioctl(fd, ATAIOGETTRACE, &agt) == -1) |
501 |
|
|
err(1, "ATAIOGETTRACE failed"); |
502 |
|
|
|
503 |
|
|
total = agt.bytes_copied; |
504 |
|
|
|
505 |
|
|
/* Parse entries */ |
506 |
|
|
while (p < total) { |
507 |
|
|
type = buf[p++]; |
508 |
|
|
if (p >= total) |
509 |
|
|
return; |
510 |
|
|
if (type <= 0 || type >= num_types) |
511 |
|
|
return; |
512 |
|
|
|
513 |
|
|
info = buf[p++]; |
514 |
|
|
if (p >= total) |
515 |
|
|
return; |
516 |
|
|
entrysize = (info & 0x1f); |
517 |
|
|
|
518 |
|
|
printf ("ch %d", (info >> 5) & 0x7); |
519 |
|
|
printf(": %s", types[type]); |
520 |
|
|
|
521 |
|
|
switch (type) { |
522 |
|
|
case WDCEVENT_STATUS: |
523 |
|
|
if (entrysize != 1) |
524 |
|
|
return; |
525 |
|
|
|
526 |
|
|
printf(": 0x%x", buf[p]); |
527 |
|
|
if (buf[p] & WDCS_BSY) |
528 |
|
|
printf(" BSY"); |
529 |
|
|
if (buf[p] & WDCS_DRDY) |
530 |
|
|
printf(" DRDY"); |
531 |
|
|
if (buf[p] & WDCS_DWF) |
532 |
|
|
printf(" DWF"); |
533 |
|
|
if (buf[p] & WDCS_DSC) |
534 |
|
|
printf(" DSC"); |
535 |
|
|
if (buf[p] & WDCS_DRQ) |
536 |
|
|
printf(" DRQ"); |
537 |
|
|
if (buf[p] & WDCS_CORR) |
538 |
|
|
printf(" CORR"); |
539 |
|
|
if (buf[p] & WDCS_IDX) |
540 |
|
|
printf(" IDX"); |
541 |
|
|
if (buf[p] & WDCS_ERR) |
542 |
|
|
printf(" ERR"); |
543 |
|
|
|
544 |
|
|
p++; |
545 |
|
|
entrysize = 0; |
546 |
|
|
break; |
547 |
|
|
case WDCEVENT_ERROR: |
548 |
|
|
if (entrysize != 1) |
549 |
|
|
return; |
550 |
|
|
|
551 |
|
|
printf(": 0x%x", buf[p]); |
552 |
|
|
if (buf[p] & WDCE_BBK) |
553 |
|
|
printf(" BBK/CRC"); |
554 |
|
|
if (buf[p] & WDCE_UNC) |
555 |
|
|
printf(" UNC"); |
556 |
|
|
if (buf[p] & WDCE_MC) |
557 |
|
|
printf(" MC"); |
558 |
|
|
if (buf[p] & WDCE_IDNF) |
559 |
|
|
printf(" IDNF"); |
560 |
|
|
if (buf[p] & WDCE_MCR) |
561 |
|
|
printf(" MCR"); |
562 |
|
|
if (buf[p] & WDCE_ABRT) |
563 |
|
|
printf(" ABRT"); |
564 |
|
|
if (buf[p] & WDCE_TK0NF) |
565 |
|
|
printf(" TK0NF"); |
566 |
|
|
if (buf[p] & WDCE_AMNF) |
567 |
|
|
printf(" AMNF"); |
568 |
|
|
|
569 |
|
|
p++; |
570 |
|
|
entrysize = 0; |
571 |
|
|
break; |
572 |
|
|
case WDCEVENT_ATAPI_CMD: |
573 |
|
|
if (entrysize < 2 || p + 2 > total) |
574 |
|
|
return; |
575 |
|
|
|
576 |
|
|
flags = (buf[p] << 8) + buf[p + 1]; |
577 |
|
|
printf(": flags 0x%x", flags); |
578 |
|
|
if (flags & 0x0100) |
579 |
|
|
printf(" MEDIA"); |
580 |
|
|
if (flags & 0x0080) |
581 |
|
|
printf(" SENSE"); |
582 |
|
|
if (flags & 0x0040) |
583 |
|
|
printf(" DMA"); |
584 |
|
|
if (flags & 0x0020) |
585 |
|
|
printf(" POLL"); |
586 |
|
|
if (flags & 0x0004) |
587 |
|
|
printf(" TIMEOUT"); |
588 |
|
|
if (flags & 0x0002) |
589 |
|
|
printf(" ATAPI"); |
590 |
|
|
|
591 |
|
|
p += 2; |
592 |
|
|
entrysize -= 2; |
593 |
|
|
break; |
594 |
|
|
case WDCEVENT_ATAPI_DONE: |
595 |
|
|
if (entrysize != 3 || p + 3 > total) |
596 |
|
|
return; |
597 |
|
|
|
598 |
|
|
flags = (buf[p] << 8) + buf[p + 1]; |
599 |
|
|
printf(": flags 0x%x", flags); |
600 |
|
|
if (flags & 0x0100) |
601 |
|
|
printf(" MEDIA"); |
602 |
|
|
if (flags & 0x0080) |
603 |
|
|
printf(" SENSE"); |
604 |
|
|
if (flags & 0x0040) |
605 |
|
|
printf(" DMA"); |
606 |
|
|
if (flags & 0x0020) |
607 |
|
|
printf(" POLL"); |
608 |
|
|
if (flags & 0x0004) |
609 |
|
|
printf(" TIMEOUT"); |
610 |
|
|
if (flags & 0x0002) |
611 |
|
|
printf(" ATAPI"); |
612 |
|
|
|
613 |
|
|
printf(", error 0x%x", buf[p + 2]); |
614 |
|
|
switch (buf[p + 2]) { |
615 |
|
|
case 1: |
616 |
|
|
printf(" (sense)"); |
617 |
|
|
break; |
618 |
|
|
case 2: |
619 |
|
|
printf(" (driver failure)"); |
620 |
|
|
break; |
621 |
|
|
case 3: |
622 |
|
|
printf(" (timeout)"); |
623 |
|
|
break; |
624 |
|
|
case 4: |
625 |
|
|
printf(" (busy)"); |
626 |
|
|
break; |
627 |
|
|
case 5: |
628 |
|
|
printf(" (ATAPI sense)"); |
629 |
|
|
break; |
630 |
|
|
case 8: |
631 |
|
|
printf(" (reset)"); |
632 |
|
|
break; |
633 |
|
|
} |
634 |
|
|
|
635 |
|
|
p += 3; |
636 |
|
|
entrysize = 0; |
637 |
|
|
break; |
638 |
|
|
case WDCEVENT_ATA_LONG: |
639 |
|
|
if (entrysize != 7 || p + 7 > total) |
640 |
|
|
return; |
641 |
|
|
|
642 |
|
|
printf(": "); |
643 |
|
|
switch (buf[p + 6]) { |
644 |
|
|
case WDCC_READDMA: |
645 |
|
|
printf("READ DMA"); |
646 |
|
|
break; |
647 |
|
|
case WDCC_WRITEDMA: |
648 |
|
|
printf("WRITE DMA"); |
649 |
|
|
break; |
650 |
|
|
default: |
651 |
|
|
printf("CMD 0x%x", buf[p + 6]); |
652 |
|
|
} |
653 |
|
|
printf(" head %d, precomp %d, cyl_hi %d, " |
654 |
|
|
"cyl_lo %d, sec %d, cnt %d", |
655 |
|
|
buf[p], buf[p + 1], buf[p + 2], buf[p + 3], |
656 |
|
|
buf[p + 4], buf[p + 5]); |
657 |
|
|
|
658 |
|
|
p += 7; |
659 |
|
|
entrysize = 0; |
660 |
|
|
break; |
661 |
|
|
case WDCEVENT_REG: |
662 |
|
|
if (entrysize != 3 || p + 3 > total) |
663 |
|
|
return; |
664 |
|
|
|
665 |
|
|
switch (buf[p]) { |
666 |
|
|
case 1: |
667 |
|
|
printf(": error"); |
668 |
|
|
break; |
669 |
|
|
case 2: |
670 |
|
|
printf(": ireason"); |
671 |
|
|
break; |
672 |
|
|
case 3: |
673 |
|
|
printf(": lba_lo"); |
674 |
|
|
break; |
675 |
|
|
case 4: |
676 |
|
|
printf(": lba_mi"); |
677 |
|
|
break; |
678 |
|
|
case 5: |
679 |
|
|
printf(": lba_hi"); |
680 |
|
|
break; |
681 |
|
|
case 6: |
682 |
|
|
printf(": sdh"); |
683 |
|
|
break; |
684 |
|
|
case 7: |
685 |
|
|
printf(": status"); |
686 |
|
|
break; |
687 |
|
|
case 8: |
688 |
|
|
printf(": altstatus"); |
689 |
|
|
break; |
690 |
|
|
default: |
691 |
|
|
printf(": unknown register %d", buf[p]); |
692 |
|
|
} |
693 |
|
|
printf(": 0x%x", (buf[p + 1] << 8) + buf[p + 2]); |
694 |
|
|
|
695 |
|
|
p += 3; |
696 |
|
|
entrysize = 0; |
697 |
|
|
break; |
698 |
|
|
case WDCEVENT_ATA_EXT: |
699 |
|
|
if (entrysize != 9 || p + 9 > total) |
700 |
|
|
return; |
701 |
|
|
|
702 |
|
|
printf(": "); |
703 |
|
|
switch (buf[p + 8]) { |
704 |
|
|
case WDCC_READDMA_EXT: |
705 |
|
|
printf("READ DMA EXT"); |
706 |
|
|
break; |
707 |
|
|
case WDCC_WRITEDMA_EXT: |
708 |
|
|
printf("WRITE DMA EXT"); |
709 |
|
|
break; |
710 |
|
|
default: |
711 |
|
|
printf("CMD 0x%x", buf[p + 8]); |
712 |
|
|
} |
713 |
|
|
printf(" lba_hi1 %d, lba_hi2 %d, " |
714 |
|
|
"lba_mi1 %d, lba_mi2 %d, lba_lo1 %d, lba_lo2 %d, " |
715 |
|
|
"count1 %d, count2 %d", |
716 |
|
|
buf[p], buf[p + 1], buf[p + 2], buf[p + 3], |
717 |
|
|
buf[p + 4], buf[p + 5], buf[p + 6], |
718 |
|
|
buf[p + 7]); |
719 |
|
|
|
720 |
|
|
p += 9; |
721 |
|
|
entrysize = 0; |
722 |
|
|
break; |
723 |
|
|
} |
724 |
|
|
|
725 |
|
|
if (entrysize > 0) |
726 |
|
|
printf(":"); |
727 |
|
|
for (i = 0; i < entrysize; i++) { |
728 |
|
|
printf (" 0x%02x", buf[p]); |
729 |
|
|
if (++p >= total) |
730 |
|
|
break; |
731 |
|
|
} |
732 |
|
|
printf("\n"); |
733 |
|
|
} |
734 |
|
|
|
735 |
|
|
return; |
736 |
|
|
|
737 |
|
|
usage: |
738 |
|
|
fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]); |
739 |
|
|
exit(1); |
740 |
|
|
} |
741 |
|
|
|
742 |
|
|
/* |
743 |
|
|
* device_identify: |
744 |
|
|
* |
745 |
|
|
* Display the identity of the device |
746 |
|
|
*/ |
747 |
|
|
void |
748 |
|
|
device_identify(int argc, char *argv[]) |
749 |
|
|
{ |
750 |
|
|
struct ataparams *inqbuf; |
751 |
|
|
struct atareq req; |
752 |
|
|
char inbuf[DEV_BSIZE]; |
753 |
|
|
u_int64_t capacity; |
754 |
|
|
u_int8_t *s; |
755 |
|
|
|
756 |
|
|
if (argc != 1) |
757 |
|
|
goto usage; |
758 |
|
|
|
759 |
|
|
memset(&inbuf, 0, sizeof(inbuf)); |
760 |
|
|
memset(&req, 0, sizeof(req)); |
761 |
|
|
|
762 |
|
|
inqbuf = (struct ataparams *) inbuf; |
763 |
|
|
|
764 |
|
|
req.flags = ATACMD_READ; |
765 |
|
|
req.command = WDCC_IDENTIFY; |
766 |
|
|
req.databuf = (caddr_t) inbuf; |
767 |
|
|
req.datalen = sizeof(inbuf); |
768 |
|
|
req.timeout = 1000; |
769 |
|
|
|
770 |
|
|
ata_command(&req); |
771 |
|
|
|
772 |
|
|
if (BYTE_ORDER == BIG_ENDIAN) { |
773 |
|
|
swap16_multi((u_int16_t *)inbuf, 10); |
774 |
|
|
swap16_multi(((u_int16_t *)inbuf) + 20, 3); |
775 |
|
|
swap16_multi(((u_int16_t *)inbuf) + 47, sizeof(inbuf) / 2 - 47); |
776 |
|
|
} |
777 |
|
|
|
778 |
|
|
if (!((inqbuf->atap_config & WDC_CFG_ATAPI_MASK) == WDC_CFG_ATAPI && |
779 |
|
|
((inqbuf->atap_model[0] == 'N' && |
780 |
|
|
inqbuf->atap_model[1] == 'E') || |
781 |
|
|
(inqbuf->atap_model[0] == 'F' && |
782 |
|
|
inqbuf->atap_model[1] == 'X')))) { |
783 |
|
|
swap16_multi((u_int16_t *)(inqbuf->atap_model), |
784 |
|
|
sizeof(inqbuf->atap_model) / 2); |
785 |
|
|
swap16_multi((u_int16_t *)(inqbuf->atap_serial), |
786 |
|
|
sizeof(inqbuf->atap_serial) / 2); |
787 |
|
|
swap16_multi((u_int16_t *)(inqbuf->atap_revision), |
788 |
|
|
sizeof(inqbuf->atap_revision) / 2); |
789 |
|
|
} |
790 |
|
|
|
791 |
|
|
/* |
792 |
|
|
* Strip blanks off of the info strings. |
793 |
|
|
*/ |
794 |
|
|
|
795 |
|
|
for (s = &inqbuf->atap_model[sizeof(inqbuf->atap_model) - 1]; |
796 |
|
|
s >= inqbuf->atap_model && *s == ' '; s--) |
797 |
|
|
*s = '\0'; |
798 |
|
|
|
799 |
|
|
for (s = &inqbuf->atap_revision[sizeof(inqbuf->atap_revision) - 1]; |
800 |
|
|
s >= inqbuf->atap_revision && *s == ' '; s--) |
801 |
|
|
*s = '\0'; |
802 |
|
|
|
803 |
|
|
for (s = &inqbuf->atap_serial[sizeof(inqbuf->atap_serial) - 1]; |
804 |
|
|
s >= inqbuf->atap_serial && *s == ' '; s--) |
805 |
|
|
*s = '\0'; |
806 |
|
|
|
807 |
|
|
printf("Model: %.*s, Rev: %.*s, Serial #: %.*s\n", |
808 |
|
|
(int) sizeof(inqbuf->atap_model), inqbuf->atap_model, |
809 |
|
|
(int) sizeof(inqbuf->atap_revision), inqbuf->atap_revision, |
810 |
|
|
(int) sizeof(inqbuf->atap_serial), inqbuf->atap_serial); |
811 |
|
|
|
812 |
|
|
printf("Device type: %s, %s\n", inqbuf->atap_config & WDC_CFG_ATAPI ? |
813 |
|
|
"ATAPI" : "ATA", inqbuf->atap_config & ATA_CFG_FIXED ? "fixed" : |
814 |
|
|
"removable"); |
815 |
|
|
|
816 |
|
|
if (inqbuf->atap_cmd2_en & ATAPI_CMD2_48AD) |
817 |
|
|
capacity = ((u_int64_t)inqbuf->atap_max_lba[3] << 48) | |
818 |
|
|
((u_int64_t)inqbuf->atap_max_lba[2] << 32) | |
819 |
|
|
((u_int64_t)inqbuf->atap_max_lba[1] << 16) | |
820 |
|
|
(u_int64_t)inqbuf->atap_max_lba[0]; |
821 |
|
|
else |
822 |
|
|
capacity = (inqbuf->atap_capacity[1] << 16) | |
823 |
|
|
inqbuf->atap_capacity[0]; |
824 |
|
|
printf("Cylinders: %d, heads: %d, sec/track: %d, total " |
825 |
|
|
"sectors: %llu\n", inqbuf->atap_cylinders, |
826 |
|
|
inqbuf->atap_heads, inqbuf->atap_sectors, capacity); |
827 |
|
|
|
828 |
|
|
if ((inqbuf->atap_cmd_set2 & ATA_CMD2_RWQ) && |
829 |
|
|
(inqbuf->atap_queuedepth & WDC_QUEUE_DEPTH_MASK)) |
830 |
|
|
printf("Device supports command queue depth of %d\n", |
831 |
|
|
(inqbuf->atap_queuedepth & WDC_QUEUE_DEPTH_MASK) + 1); |
832 |
|
|
|
833 |
|
|
printf("Device capabilities:\n"); |
834 |
|
|
print_bitinfo("\t%s\n", inqbuf->atap_capabilities1, ata_caps); |
835 |
|
|
|
836 |
|
|
if (inqbuf->atap_ata_major != 0 && inqbuf->atap_ata_major != 0xffff) { |
837 |
|
|
printf("Device supports the following standards:\n"); |
838 |
|
|
print_bitinfo("%s ", inqbuf->atap_ata_major, ata_vers); |
839 |
|
|
printf("\n"); |
840 |
|
|
} |
841 |
|
|
|
842 |
|
|
if ((inqbuf->atap_cmd_set1 & WDC_CMD1_SEC) && |
843 |
|
|
inqbuf->atap_mpasswd_rev != 0 && |
844 |
|
|
inqbuf->atap_mpasswd_rev != 0xffff) |
845 |
|
|
printf("Master password revision code 0x%04x\n", |
846 |
|
|
inqbuf->atap_mpasswd_rev); |
847 |
|
|
|
848 |
|
|
if (inqbuf->atap_cmd_set1 != 0 && inqbuf->atap_cmd_set1 != 0xffff && |
849 |
|
|
inqbuf->atap_cmd_set2 != 0 && inqbuf->atap_cmd_set2 != 0xffff) { |
850 |
|
|
printf("Device supports the following command sets:\n"); |
851 |
|
|
print_bitinfo("\t%s\n", inqbuf->atap_cmd_set1, ata_cmd_set1); |
852 |
|
|
print_bitinfo("\t%s\n", inqbuf->atap_cmd_set2, ata_cmd_set2); |
853 |
|
|
print_bitinfo("\t%s\n", inqbuf->atap_cmd_ext, ata_cmd_ext); |
854 |
|
|
} |
855 |
|
|
|
856 |
|
|
if (inqbuf->atap_cmd_def != 0 && inqbuf->atap_cmd_def != 0xffff) { |
857 |
|
|
printf("Device has enabled the following command " |
858 |
|
|
"sets/features:\n"); |
859 |
|
|
print_bitinfo("\t%s\n", inqbuf->atap_cmd1_en, ata_cmd_set1); |
860 |
|
|
print_bitinfo("\t%s\n", inqbuf->atap_cmd2_en, ata_cmd_set2); |
861 |
|
|
} |
862 |
|
|
|
863 |
|
|
return; |
864 |
|
|
|
865 |
|
|
usage: |
866 |
|
|
fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]); |
867 |
|
|
exit(1); |
868 |
|
|
} |
869 |
|
|
|
870 |
|
|
/* |
871 |
|
|
* device idle: |
872 |
|
|
* |
873 |
|
|
* issue the IDLE IMMEDIATE command to the drive |
874 |
|
|
*/ |
875 |
|
|
void |
876 |
|
|
device_idle(int argc, char *argv[]) |
877 |
|
|
{ |
878 |
|
|
struct atareq req; |
879 |
|
|
|
880 |
|
|
if (argc != 1) |
881 |
|
|
goto usage; |
882 |
|
|
|
883 |
|
|
memset(&req, 0, sizeof(req)); |
884 |
|
|
|
885 |
|
|
if (strcmp(argv[0], "idle") == 0) |
886 |
|
|
req.command = WDCC_IDLE_IMMED; |
887 |
|
|
else if (strcmp(argv[0], "standby") == 0) |
888 |
|
|
req.command = WDCC_STANDBY_IMMED; |
889 |
|
|
else |
890 |
|
|
req.command = WDCC_SLEEP; |
891 |
|
|
|
892 |
|
|
req.timeout = 1000; |
893 |
|
|
|
894 |
|
|
ata_command(&req); |
895 |
|
|
|
896 |
|
|
return; |
897 |
|
|
usage: |
898 |
|
|
fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]); |
899 |
|
|
exit(1); |
900 |
|
|
} |
901 |
|
|
|
902 |
|
|
/* |
903 |
|
|
* SECURITY SET PASSWORD command |
904 |
|
|
*/ |
905 |
|
|
void |
906 |
|
|
device_sec_setpass(int argc, char *argv[]) |
907 |
|
|
{ |
908 |
|
|
struct atareq req; |
909 |
|
|
struct sec_password pwd; |
910 |
|
|
char *pass, inbuf[DEV_BSIZE]; |
911 |
|
|
struct ataparams *inqbuf = (struct ataparams *)inbuf; |
912 |
|
|
|
913 |
|
|
if (argc < 2) |
914 |
|
|
goto usage; |
915 |
|
|
|
916 |
|
|
memset(&pwd, 0, sizeof(pwd)); |
917 |
|
|
|
918 |
|
|
if (strcmp(argv[1], "user") == 0 && argc == 3) |
919 |
|
|
pwd.ctrl |= SEC_PASSWORD_USER; |
920 |
|
|
else if (strcmp(argv[1], "master") == 0 && argc == 2) |
921 |
|
|
pwd.ctrl |= SEC_PASSWORD_MASTER; |
922 |
|
|
else |
923 |
|
|
goto usage; |
924 |
|
|
if (argc == 3) { |
925 |
|
|
if (strcmp(argv[2], "high") == 0) |
926 |
|
|
pwd.ctrl |= SEC_LEVEL_HIGH; |
927 |
|
|
else if (strcmp(argv[2], "maximum") == 0) |
928 |
|
|
pwd.ctrl |= SEC_LEVEL_MAX; |
929 |
|
|
else |
930 |
|
|
goto usage; |
931 |
|
|
} |
932 |
|
|
|
933 |
|
|
/* |
934 |
|
|
* Issue IDENTIFY command to obtain master password |
935 |
|
|
* revision code and decrement its value. |
936 |
|
|
* The valid revision codes are 0x0001 through 0xfffe. |
937 |
|
|
* If the device returns 0x0000 or 0xffff as a revision |
938 |
|
|
* code then the master password revision code is not |
939 |
|
|
* supported so don't touch it. |
940 |
|
|
*/ |
941 |
|
|
memset(&inbuf, 0, sizeof(inbuf)); |
942 |
|
|
memset(&req, 0, sizeof(req)); |
943 |
|
|
|
944 |
|
|
req.command = WDCC_IDENTIFY; |
945 |
|
|
req.timeout = 1000; |
946 |
|
|
req.flags = ATACMD_READ; |
947 |
|
|
req.databuf = (caddr_t)inbuf; |
948 |
|
|
req.datalen = sizeof(inbuf); |
949 |
|
|
|
950 |
|
|
ata_command(&req); |
951 |
|
|
|
952 |
|
|
pwd.revision = inqbuf->atap_mpasswd_rev; |
953 |
|
|
if (pwd.revision != 0 && pwd.revision != 0xffff && --pwd.revision == 0) |
954 |
|
|
pwd.revision = 0xfffe; |
955 |
|
|
|
956 |
|
|
pass = sec_getpass(pwd.ctrl & SEC_PASSWORD_MASTER, 1); |
957 |
|
|
memcpy(pwd.password, pass, strlen(pass)); |
958 |
|
|
|
959 |
|
|
memset(&req, 0, sizeof(req)); |
960 |
|
|
|
961 |
|
|
req.command = ATA_SEC_SET_PASSWORD; |
962 |
|
|
req.timeout = 1000; |
963 |
|
|
req.flags = ATACMD_WRITE; |
964 |
|
|
req.databuf = (caddr_t)&pwd; |
965 |
|
|
req.datalen = sizeof(pwd); |
966 |
|
|
|
967 |
|
|
ata_command(&req); |
968 |
|
|
|
969 |
|
|
return; |
970 |
|
|
usage: |
971 |
|
|
fprintf(stderr, "usage: %s device %s user high | maximum\n", |
972 |
|
|
__progname, argv[0]); |
973 |
|
|
fprintf(stderr, " %s device %s master\n", __progname, argv[0]); |
974 |
|
|
exit(1); |
975 |
|
|
} |
976 |
|
|
|
977 |
|
|
/* |
978 |
|
|
* SECURITY UNLOCK command |
979 |
|
|
*/ |
980 |
|
|
void |
981 |
|
|
device_sec_unlock(int argc, char *argv[]) |
982 |
|
|
{ |
983 |
|
|
struct atareq req; |
984 |
|
|
struct sec_password pwd; |
985 |
|
|
char *pass; |
986 |
|
|
|
987 |
|
|
if (argc != 2) |
988 |
|
|
goto usage; |
989 |
|
|
|
990 |
|
|
memset(&pwd, 0, sizeof(pwd)); |
991 |
|
|
|
992 |
|
|
if (strcmp(argv[1], "user") == 0) |
993 |
|
|
pwd.ctrl |= SEC_PASSWORD_USER; |
994 |
|
|
else if (strcmp(argv[1], "master") == 0) |
995 |
|
|
pwd.ctrl |= SEC_PASSWORD_MASTER; |
996 |
|
|
else |
997 |
|
|
goto usage; |
998 |
|
|
|
999 |
|
|
pass = sec_getpass(pwd.ctrl & SEC_PASSWORD_MASTER, 0); |
1000 |
|
|
memcpy(pwd.password, pass, strlen(pass)); |
1001 |
|
|
|
1002 |
|
|
memset(&req, 0, sizeof(req)); |
1003 |
|
|
|
1004 |
|
|
req.command = ATA_SEC_UNLOCK; |
1005 |
|
|
req.timeout = 1000; |
1006 |
|
|
req.flags = ATACMD_WRITE; |
1007 |
|
|
req.databuf = (caddr_t)&pwd; |
1008 |
|
|
req.datalen = sizeof(pwd); |
1009 |
|
|
|
1010 |
|
|
ata_command(&req); |
1011 |
|
|
|
1012 |
|
|
return; |
1013 |
|
|
usage: |
1014 |
|
|
fprintf(stderr, "usage: %s device %s user | master\n", __progname, |
1015 |
|
|
argv[0]); |
1016 |
|
|
exit(1); |
1017 |
|
|
} |
1018 |
|
|
|
1019 |
|
|
/* |
1020 |
|
|
* SECURITY ERASE UNIT command |
1021 |
|
|
*/ |
1022 |
|
|
void |
1023 |
|
|
device_sec_erase(int argc, char *argv[]) |
1024 |
|
|
{ |
1025 |
|
|
struct atareq req; |
1026 |
|
|
struct sec_password pwd; |
1027 |
|
|
char *pass; |
1028 |
|
|
|
1029 |
|
|
if (argc < 2) |
1030 |
|
|
goto usage; |
1031 |
|
|
|
1032 |
|
|
memset(&pwd, 0, sizeof(pwd)); |
1033 |
|
|
|
1034 |
|
|
if (strcmp(argv[1], "user") == 0) |
1035 |
|
|
pwd.ctrl |= SEC_PASSWORD_USER; |
1036 |
|
|
else if (strcmp(argv[1], "master") == 0) |
1037 |
|
|
pwd.ctrl |= SEC_PASSWORD_MASTER; |
1038 |
|
|
else |
1039 |
|
|
goto usage; |
1040 |
|
|
if (argc == 2) |
1041 |
|
|
pwd.ctrl |= SEC_ERASE_NORMAL; |
1042 |
|
|
else if (argc == 3 && strcmp(argv[2], "enhanced") == 0) |
1043 |
|
|
pwd.ctrl |= SEC_ERASE_ENHANCED; |
1044 |
|
|
else |
1045 |
|
|
goto usage; |
1046 |
|
|
|
1047 |
|
|
pass = sec_getpass(pwd.ctrl & SEC_PASSWORD_MASTER, 0); |
1048 |
|
|
memcpy(pwd.password, pass, strlen(pass)); |
1049 |
|
|
|
1050 |
|
|
/* Issue SECURITY ERASE PREPARE command before */ |
1051 |
|
|
memset(&req, 0, sizeof(req)); |
1052 |
|
|
|
1053 |
|
|
req.command = ATA_SEC_ERASE_PREPARE; |
1054 |
|
|
req.timeout = 1000; |
1055 |
|
|
|
1056 |
|
|
ata_command(&req); |
1057 |
|
|
|
1058 |
|
|
memset(&req, 0, sizeof(req)); |
1059 |
|
|
|
1060 |
|
|
req.command = ATA_SEC_ERASE_UNIT; |
1061 |
|
|
req.timeout = 1000; |
1062 |
|
|
req.flags = ATACMD_WRITE; |
1063 |
|
|
req.databuf = (caddr_t)&pwd; |
1064 |
|
|
req.datalen = sizeof(pwd); |
1065 |
|
|
|
1066 |
|
|
ata_command(&req); |
1067 |
|
|
|
1068 |
|
|
return; |
1069 |
|
|
usage: |
1070 |
|
|
fprintf(stderr, "usage: %s device %s user | master [enhanced]\n", |
1071 |
|
|
__progname, argv[0]); |
1072 |
|
|
exit(1); |
1073 |
|
|
} |
1074 |
|
|
|
1075 |
|
|
/* |
1076 |
|
|
* SECURITY FREEZE LOCK command |
1077 |
|
|
*/ |
1078 |
|
|
void |
1079 |
|
|
device_sec_freeze(int argc, char *argv[]) |
1080 |
|
|
{ |
1081 |
|
|
struct atareq req; |
1082 |
|
|
|
1083 |
|
|
if (argc != 1) |
1084 |
|
|
goto usage; |
1085 |
|
|
|
1086 |
|
|
memset(&req, 0, sizeof(req)); |
1087 |
|
|
|
1088 |
|
|
req.command = ATA_SEC_FREEZE_LOCK; |
1089 |
|
|
req.timeout = 1000; |
1090 |
|
|
|
1091 |
|
|
ata_command(&req); |
1092 |
|
|
|
1093 |
|
|
return; |
1094 |
|
|
usage: |
1095 |
|
|
fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]); |
1096 |
|
|
exit(1); |
1097 |
|
|
} |
1098 |
|
|
|
1099 |
|
|
/* |
1100 |
|
|
* SECURITY DISABLE PASSWORD command |
1101 |
|
|
*/ |
1102 |
|
|
void |
1103 |
|
|
device_sec_disablepass(int argc, char *argv[]) |
1104 |
|
|
{ |
1105 |
|
|
struct atareq req; |
1106 |
|
|
struct sec_password pwd; |
1107 |
|
|
char *pass; |
1108 |
|
|
|
1109 |
|
|
if (argc != 2) |
1110 |
|
|
goto usage; |
1111 |
|
|
|
1112 |
|
|
memset(&pwd, 0, sizeof(pwd)); |
1113 |
|
|
|
1114 |
|
|
if (strcmp(argv[1], "user") == 0) |
1115 |
|
|
pwd.ctrl |= SEC_PASSWORD_USER; |
1116 |
|
|
else if (strcmp(argv[1], "master") == 0) |
1117 |
|
|
pwd.ctrl |= SEC_PASSWORD_MASTER; |
1118 |
|
|
else |
1119 |
|
|
goto usage; |
1120 |
|
|
|
1121 |
|
|
pass = sec_getpass(pwd.ctrl & SEC_PASSWORD_MASTER, 0); |
1122 |
|
|
memcpy(pwd.password, pass, strlen(pass)); |
1123 |
|
|
|
1124 |
|
|
memset(&req, 0, sizeof(req)); |
1125 |
|
|
|
1126 |
|
|
req.command = ATA_SEC_DISABLE_PASSWORD; |
1127 |
|
|
req.timeout = 1000; |
1128 |
|
|
req.flags = ATACMD_WRITE; |
1129 |
|
|
req.databuf = (caddr_t)&pwd; |
1130 |
|
|
req.datalen = sizeof(pwd); |
1131 |
|
|
|
1132 |
|
|
ata_command(&req); |
1133 |
|
|
|
1134 |
|
|
return; |
1135 |
|
|
usage: |
1136 |
|
|
fprintf(stderr, "usage: %s device %s user | master\n", __progname, |
1137 |
|
|
argv[0]); |
1138 |
|
|
exit(1); |
1139 |
|
|
} |
1140 |
|
|
|
1141 |
|
|
char * |
1142 |
|
|
sec_getpass(int ident, int confirm) |
1143 |
|
|
{ |
1144 |
|
|
char *pass, buf[33]; |
1145 |
|
|
|
1146 |
|
|
if ((pass = getpass(ident ? "Master password:" : |
1147 |
|
|
"User password:")) == NULL) |
1148 |
|
|
err(1, "getpass()"); |
1149 |
|
|
if (strlen(pass) > 32) |
1150 |
|
|
errx(1, "password too long"); |
1151 |
|
|
if (confirm) { |
1152 |
|
|
strlcpy(buf, pass, sizeof(buf)); |
1153 |
|
|
if ((pass = getpass(ident ? "Retype master password:" : |
1154 |
|
|
"Retype user password:")) == NULL) |
1155 |
|
|
err(1, "getpass()"); |
1156 |
|
|
if (strcmp(pass, buf) != 0) |
1157 |
|
|
errx(1, "password mismatch"); |
1158 |
|
|
} |
1159 |
|
|
|
1160 |
|
|
return (pass); |
1161 |
|
|
} |
1162 |
|
|
|
1163 |
|
|
/* |
1164 |
|
|
* SMART ENABLE OPERATIONS command |
1165 |
|
|
*/ |
1166 |
|
|
void |
1167 |
|
|
device_smart_enable(int argc, char *argv[]) |
1168 |
|
|
{ |
1169 |
|
|
struct atareq req; |
1170 |
|
|
|
1171 |
|
|
if (argc != 1) |
1172 |
|
|
goto usage; |
1173 |
|
|
|
1174 |
|
|
memset(&req, 0, sizeof(req)); |
1175 |
|
|
|
1176 |
|
|
req.command = ATAPI_SMART; |
1177 |
|
|
req.cylinder = 0xc24f; |
1178 |
|
|
req.timeout = 1000; |
1179 |
|
|
req.features = ATA_SMART_EN; |
1180 |
|
|
|
1181 |
|
|
ata_command(&req); |
1182 |
|
|
|
1183 |
|
|
return; |
1184 |
|
|
usage: |
1185 |
|
|
fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]); |
1186 |
|
|
exit(1); |
1187 |
|
|
} |
1188 |
|
|
|
1189 |
|
|
/* |
1190 |
|
|
* SMART DISABLE OPERATIONS command |
1191 |
|
|
*/ |
1192 |
|
|
void |
1193 |
|
|
device_smart_disable(int argc, char *argv[]) |
1194 |
|
|
{ |
1195 |
|
|
struct atareq req; |
1196 |
|
|
|
1197 |
|
|
if (argc != 1) |
1198 |
|
|
goto usage; |
1199 |
|
|
|
1200 |
|
|
memset(&req, 0, sizeof(req)); |
1201 |
|
|
|
1202 |
|
|
req.command = ATAPI_SMART; |
1203 |
|
|
req.cylinder = 0xc24f; |
1204 |
|
|
req.timeout = 1000; |
1205 |
|
|
req.features = ATA_SMART_DS; |
1206 |
|
|
|
1207 |
|
|
ata_command(&req); |
1208 |
|
|
|
1209 |
|
|
return; |
1210 |
|
|
usage: |
1211 |
|
|
fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]); |
1212 |
|
|
exit(1); |
1213 |
|
|
} |
1214 |
|
|
|
1215 |
|
|
/* |
1216 |
|
|
* SMART STATUS command |
1217 |
|
|
*/ |
1218 |
|
|
void |
1219 |
|
|
device_smart_status(int argc, char *argv[]) |
1220 |
|
|
{ |
1221 |
|
|
struct atareq req; |
1222 |
|
|
|
1223 |
|
|
if (argc != 1) |
1224 |
|
|
goto usage; |
1225 |
|
|
|
1226 |
|
|
memset(&req, 0, sizeof(req)); |
1227 |
|
|
|
1228 |
|
|
req.command = ATAPI_SMART; |
1229 |
|
|
req.cylinder = 0xc24f; |
1230 |
|
|
req.timeout = 1000; |
1231 |
|
|
req.features = ATA_SMART_STATUS; |
1232 |
|
|
|
1233 |
|
|
ata_command(&req); |
1234 |
|
|
|
1235 |
|
|
if (req.cylinder == 0xc24f) |
1236 |
|
|
printf("No SMART threshold exceeded\n"); |
1237 |
|
|
else if (req.cylinder == 0x2cf4) { |
1238 |
|
|
errx(2, "SMART threshold exceeded!"); |
1239 |
|
|
} else { |
1240 |
|
|
errx(1, "Unknown response %02x!", req.cylinder); |
1241 |
|
|
} |
1242 |
|
|
|
1243 |
|
|
return; |
1244 |
|
|
usage: |
1245 |
|
|
fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]); |
1246 |
|
|
exit(1); |
1247 |
|
|
} |
1248 |
|
|
|
1249 |
|
|
/* |
1250 |
|
|
* SMART ENABLE/DISABLE ATTRIBUTE AUTOSAVE command |
1251 |
|
|
*/ |
1252 |
|
|
void |
1253 |
|
|
device_smart_autosave(int argc, char *argv[]) |
1254 |
|
|
{ |
1255 |
|
|
struct atareq req; |
1256 |
|
|
int val; |
1257 |
|
|
|
1258 |
|
|
if (argc != 2) |
1259 |
|
|
goto usage; |
1260 |
|
|
|
1261 |
|
|
memset(&req, 0, sizeof(req)); |
1262 |
|
|
|
1263 |
|
|
req.command = ATAPI_SMART; |
1264 |
|
|
req.cylinder = 0xc24f; |
1265 |
|
|
req.timeout = 1000; |
1266 |
|
|
req.features = ATA_SMART_AUTOSAVE; |
1267 |
|
|
if ((val = strtoval(argv[1], smart_autosave)) == -1) |
1268 |
|
|
goto usage; |
1269 |
|
|
req.sec_num = val; |
1270 |
|
|
|
1271 |
|
|
ata_command(&req); |
1272 |
|
|
|
1273 |
|
|
return; |
1274 |
|
|
usage: |
1275 |
|
|
fprintf(stderr, "usage: %s device %s enable | disable\n", __progname, |
1276 |
|
|
argv[0]); |
1277 |
|
|
exit(1); |
1278 |
|
|
} |
1279 |
|
|
|
1280 |
|
|
/* |
1281 |
|
|
* SMART EXECUTE OFF-LINE IMMEDIATE command |
1282 |
|
|
*/ |
1283 |
|
|
void |
1284 |
|
|
device_smart_offline(int argc, char *argv[]) |
1285 |
|
|
{ |
1286 |
|
|
struct atareq req; |
1287 |
|
|
int val; |
1288 |
|
|
|
1289 |
|
|
if (argc != 2) |
1290 |
|
|
goto usage; |
1291 |
|
|
|
1292 |
|
|
memset(&req, 0, sizeof(req)); |
1293 |
|
|
|
1294 |
|
|
req.command = ATAPI_SMART; |
1295 |
|
|
req.cylinder = 0xc24f; |
1296 |
|
|
req.timeout = 1000; |
1297 |
|
|
req.features = ATA_SMART_OFFLINE; |
1298 |
|
|
if ((val = strtoval(argv[1], smart_offline)) == -1) |
1299 |
|
|
goto usage; |
1300 |
|
|
req.sec_num = val; |
1301 |
|
|
|
1302 |
|
|
ata_command(&req); |
1303 |
|
|
|
1304 |
|
|
return; |
1305 |
|
|
usage: |
1306 |
|
|
fprintf(stderr, "usage: %s device %s subcommand\n", __progname, |
1307 |
|
|
argv[0]); |
1308 |
|
|
exit(1); |
1309 |
|
|
} |
1310 |
|
|
|
1311 |
|
|
/* |
1312 |
|
|
* SMART READ DATA command |
1313 |
|
|
*/ |
1314 |
|
|
void |
1315 |
|
|
device_smart_read(int argc, char *argv[]) |
1316 |
|
|
{ |
1317 |
|
|
struct atareq req; |
1318 |
|
|
struct smart_read data; |
1319 |
|
|
|
1320 |
|
|
if (argc != 1) |
1321 |
|
|
goto usage; |
1322 |
|
|
|
1323 |
|
|
memset(&req, 0, sizeof(req)); |
1324 |
|
|
memset(&data, 0, sizeof(data)); |
1325 |
|
|
|
1326 |
|
|
req.command = ATAPI_SMART; |
1327 |
|
|
req.cylinder = 0xc24f; |
1328 |
|
|
req.timeout = 1000; |
1329 |
|
|
req.features = ATA_SMART_READ; |
1330 |
|
|
req.flags = ATACMD_READ; |
1331 |
|
|
req.databuf = (caddr_t)&data; |
1332 |
|
|
req.datalen = sizeof(data); |
1333 |
|
|
|
1334 |
|
|
ata_command(&req); |
1335 |
|
|
|
1336 |
|
|
if (smart_cksum((u_int8_t *)&data, sizeof(data)) != 0) |
1337 |
|
|
errx(1, "Checksum mismatch"); |
1338 |
|
|
|
1339 |
|
|
printf("Off-line data collection:\n"); |
1340 |
|
|
printf(" status: %s\n", |
1341 |
|
|
valtostr(data.offstat & 0x7f, smart_offstat)); |
1342 |
|
|
printf(" activity completion time: %d seconds\n", |
1343 |
|
|
letoh16(data.time)); |
1344 |
|
|
printf(" capabilities:\n"); |
1345 |
|
|
print_bitinfo("\t%s\n", data.offcap, smart_offcap); |
1346 |
|
|
printf("Self-test execution:\n"); |
1347 |
|
|
printf(" status: %s\n", valtostr(SMART_SELFSTAT_STAT(data.selfstat), |
1348 |
|
|
smart_selfstat)); |
1349 |
|
|
if (SMART_SELFSTAT_STAT(data.selfstat) == SMART_SELFSTAT_PROGRESS) |
1350 |
|
|
printf("remains %d%% of total time\n", |
1351 |
|
|
SMART_SELFSTAT_PCNT(data.selfstat)); |
1352 |
|
|
printf(" recommended polling time:\n"); |
1353 |
|
|
printf("\tshort routine: %d minutes\n", data.shtime); |
1354 |
|
|
printf("\textended routine: %d minutes\n", data.extime); |
1355 |
|
|
printf("SMART capabilities:\n"); |
1356 |
|
|
print_bitinfo(" %s\n", letoh16(data.smartcap), smart_smartcap); |
1357 |
|
|
printf("Error logging: "); |
1358 |
|
|
if (data.errcap & SMART_ERRCAP_ERRLOG) |
1359 |
|
|
printf("supported\n"); |
1360 |
|
|
else |
1361 |
|
|
printf("not supported\n"); |
1362 |
|
|
|
1363 |
|
|
return; |
1364 |
|
|
usage: |
1365 |
|
|
fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]); |
1366 |
|
|
exit(1); |
1367 |
|
|
} |
1368 |
|
|
|
1369 |
|
|
/* |
1370 |
|
|
* SMART READ LOG command |
1371 |
|
|
*/ |
1372 |
|
|
void |
1373 |
|
|
device_smart_readlog(int argc, char *argv[]) |
1374 |
|
|
{ |
1375 |
|
|
struct atareq req; |
1376 |
|
|
int val; |
1377 |
|
|
u_int8_t inbuf[DEV_BSIZE]; |
1378 |
|
|
|
1379 |
|
|
if (argc != 2) |
1380 |
|
|
goto usage; |
1381 |
|
|
|
1382 |
|
|
memset(&req, 0, sizeof(req)); |
1383 |
|
|
memset(&inbuf, 0, sizeof(inbuf)); |
1384 |
|
|
|
1385 |
|
|
req.command = ATAPI_SMART; |
1386 |
|
|
req.cylinder = 0xc24f; |
1387 |
|
|
req.timeout = 1000; |
1388 |
|
|
req.features = ATA_SMART_READLOG; |
1389 |
|
|
req.flags = ATACMD_READ; |
1390 |
|
|
req.sec_count = 1; |
1391 |
|
|
req.databuf = (caddr_t)inbuf; |
1392 |
|
|
req.datalen = sizeof(inbuf); |
1393 |
|
|
if ((val = strtoval(argv[1], smart_readlog)) == -1) |
1394 |
|
|
goto usage; |
1395 |
|
|
req.sec_num = val; |
1396 |
|
|
|
1397 |
|
|
ata_command(&req); |
1398 |
|
|
|
1399 |
|
|
if (strcmp(argv[1], "directory") == 0) { |
1400 |
|
|
struct smart_log_dir *data = (struct smart_log_dir *)inbuf; |
1401 |
|
|
int i; |
1402 |
|
|
|
1403 |
|
|
if (data->version != SMART_LOG_MSECT) { |
1404 |
|
|
printf("Device doesn't support multi-sector logs\n"); |
1405 |
|
|
return; |
1406 |
|
|
} |
1407 |
|
|
|
1408 |
|
|
for (i = 0; i < 255; i++) |
1409 |
|
|
printf("Log address %d: %d sectors\n", i + 1, |
1410 |
|
|
data->entry[i].sec_num); |
1411 |
|
|
} else if (strcmp(argv[1], "summary") == 0) { |
1412 |
|
|
struct smart_log_sum *data = (struct smart_log_sum *)inbuf; |
1413 |
|
|
int i, n, nerr; |
1414 |
|
|
|
1415 |
|
|
if (smart_cksum(inbuf, sizeof(inbuf)) != 0) |
1416 |
|
|
errx(1, "Checksum mismatch"); |
1417 |
|
|
|
1418 |
|
|
if (data->index == 0) { |
1419 |
|
|
printf("No log entries\n"); |
1420 |
|
|
return; |
1421 |
|
|
} |
1422 |
|
|
|
1423 |
|
|
nerr = letoh16(data->err_cnt); |
1424 |
|
|
printf("Error count: %d\n\n", nerr); |
1425 |
|
|
/* |
1426 |
|
|
* Five error log data structures form a circular |
1427 |
|
|
* buffer. data->index points to the most recent |
1428 |
|
|
* record and err_cnt contains total error number. |
1429 |
|
|
* We pass from the most recent record to the |
1430 |
|
|
* latest one. |
1431 |
|
|
*/ |
1432 |
|
|
i = data->index - 1; |
1433 |
|
|
n = 0; |
1434 |
|
|
do { |
1435 |
|
|
printf("Error %d:\n", n + 1); |
1436 |
|
|
smart_print_errdata(&data->errdata[i--]); |
1437 |
|
|
if (i == -1) |
1438 |
|
|
i = 4; |
1439 |
|
|
} while (++n < (nerr > 5 ? 5 : nerr)); |
1440 |
|
|
} else if (strcmp(argv[1], "comp") == 0) { |
1441 |
|
|
struct smart_log_comp *data = (struct smart_log_comp *)inbuf; |
1442 |
|
|
u_int8_t *newbuf; |
1443 |
|
|
int i, n, nerr, nsect; |
1444 |
|
|
|
1445 |
|
|
if (smart_cksum(inbuf, sizeof(inbuf)) != 0) |
1446 |
|
|
errx(1, "Checksum mismatch"); |
1447 |
|
|
|
1448 |
|
|
if (data->index == 0) { |
1449 |
|
|
printf("No log entries\n"); |
1450 |
|
|
return; |
1451 |
|
|
} |
1452 |
|
|
|
1453 |
|
|
i = data->index - 1; |
1454 |
|
|
nerr = letoh16(data->err_cnt); |
1455 |
|
|
printf("Error count: %d\n", nerr); |
1456 |
|
|
/* |
1457 |
|
|
* From the first sector we obtain total error number |
1458 |
|
|
* and calculate necessary number of sectors to read. |
1459 |
|
|
* All read error data structures form a circular |
1460 |
|
|
* buffer and we pass from the most recent record to |
1461 |
|
|
* the latest one. |
1462 |
|
|
*/ |
1463 |
|
|
nsect = nerr / 5 + (nerr % 5 != 0 ? 1 : 0); |
1464 |
|
|
if ((newbuf = calloc(nsect, DEV_BSIZE)) == NULL) |
1465 |
|
|
err(1, "calloc()"); |
1466 |
|
|
memset(&req, 0, sizeof(req)); |
1467 |
|
|
req.flags = ATACMD_READ; |
1468 |
|
|
req.command = ATAPI_SMART; |
1469 |
|
|
req.features = ATA_SMART_READLOG; |
1470 |
|
|
req.sec_count = nsect; |
1471 |
|
|
req.sec_num = SMART_READLOG_COMP; |
1472 |
|
|
req.cylinder = 0xc24f; |
1473 |
|
|
req.databuf = (caddr_t)newbuf; |
1474 |
|
|
req.datalen = nsect * DEV_BSIZE; |
1475 |
|
|
req.timeout = 1000; |
1476 |
|
|
ata_command(&req); |
1477 |
|
|
|
1478 |
|
|
n = 0; |
1479 |
|
|
data = (struct smart_log_comp *) |
1480 |
|
|
(newbuf + (nsect - 1) * DEV_BSIZE); |
1481 |
|
|
do { |
1482 |
|
|
printf("Error %d:\n", n + 1); |
1483 |
|
|
smart_print_errdata(&data->errdata[i-- % 5]); |
1484 |
|
|
if (i == -1) |
1485 |
|
|
i = 254; |
1486 |
|
|
if (i % 5 == 4) |
1487 |
|
|
data = (struct smart_log_comp *) |
1488 |
|
|
(newbuf + (i / 5) * DEV_BSIZE); |
1489 |
|
|
} while (++n < nerr); |
1490 |
|
|
} else if (strcmp(argv[1], "selftest") == 0) { |
1491 |
|
|
struct smart_log_self *data = (struct smart_log_self *)inbuf; |
1492 |
|
|
int i, n; |
1493 |
|
|
|
1494 |
|
|
if (smart_cksum(inbuf, sizeof(inbuf)) != 0) |
1495 |
|
|
errx(1, "Checksum mismatch"); |
1496 |
|
|
|
1497 |
|
|
if (data->index == 0) { |
1498 |
|
|
printf("No log entries\n"); |
1499 |
|
|
return; |
1500 |
|
|
} |
1501 |
|
|
|
1502 |
|
|
/* circular buffer of 21 entries */ |
1503 |
|
|
i = data->index - 1; |
1504 |
|
|
n = 0; |
1505 |
|
|
do { |
1506 |
|
|
/* don't print empty entries */ |
1507 |
|
|
if ((data->desc[i].time1 | data->desc[i].time2) == 0) |
1508 |
|
|
break; |
1509 |
|
|
printf("Test %d\n", n + 1); |
1510 |
|
|
printf(" LBA Low: 0x%x\n", data->desc[i].reg_lbalo); |
1511 |
|
|
printf(" status: %s\n", |
1512 |
|
|
valtostr(SMART_SELFSTAT_STAT( |
1513 |
|
|
data->desc[i].selfstat), |
1514 |
|
|
smart_selfstat)); |
1515 |
|
|
printf(" timestamp: %d\n", |
1516 |
|
|
MAKEWORD(data->desc[i].time1, |
1517 |
|
|
data->desc[i].time2)); |
1518 |
|
|
printf(" failure checkpoint byte: 0x%x\n", |
1519 |
|
|
data->desc[i].chkpnt); |
1520 |
|
|
printf(" failing LBA: 0x%x\n", |
1521 |
|
|
MAKEDWORD(data->desc[i].lbafail1, |
1522 |
|
|
data->desc[i].lbafail2, |
1523 |
|
|
data->desc[i].lbafail3, |
1524 |
|
|
data->desc[i].lbafail4)); |
1525 |
|
|
if (--i == -1) |
1526 |
|
|
i = 20; |
1527 |
|
|
} while (++n < 21); |
1528 |
|
|
} |
1529 |
|
|
|
1530 |
|
|
return; |
1531 |
|
|
usage: |
1532 |
|
|
fprintf(stderr, "usage: %s device %s log\n", __progname, argv[0]); |
1533 |
|
|
exit(1); |
1534 |
|
|
} |
1535 |
|
|
|
1536 |
|
|
#define SMART_PRINTREG(str, reg) \ |
1537 |
|
|
printf(str "0x%02x\t0x%02x\t0x%02x\t0x%02x\t0x%02x\n", \ |
1538 |
|
|
data->cmd[0].reg, \ |
1539 |
|
|
data->cmd[1].reg, \ |
1540 |
|
|
data->cmd[2].reg, \ |
1541 |
|
|
data->cmd[3].reg, \ |
1542 |
|
|
data->cmd[4].reg) |
1543 |
|
|
|
1544 |
|
|
void |
1545 |
|
|
smart_print_errdata(struct smart_log_errdata *data) |
1546 |
|
|
{ |
1547 |
|
|
printf(" error register: 0x%x\n", data->err.reg_err); |
1548 |
|
|
printf(" sector count register: 0x%x\n", data->err.reg_seccnt); |
1549 |
|
|
printf(" LBA Low register: 0x%x\n", data->err.reg_lbalo); |
1550 |
|
|
printf(" LBA Mid register: 0x%x\n", data->err.reg_lbamid); |
1551 |
|
|
printf(" LBA High register: 0x%x\n", data->err.reg_lbahi); |
1552 |
|
|
printf(" device register: 0x%x\n", data->err.reg_dev); |
1553 |
|
|
printf(" status register: 0x%x\n", data->err.reg_stat); |
1554 |
|
|
printf(" state: %s\n", valtostr(data->err.state, smart_logstat)); |
1555 |
|
|
printf(" timestamp: %d\n", MAKEWORD(data->err.time1, |
1556 |
|
|
data->err.time2)); |
1557 |
|
|
printf(" history:\n"); |
1558 |
|
|
SMART_PRINTREG("\tcontrol register:\t", reg_ctl); |
1559 |
|
|
SMART_PRINTREG("\tfeatures register:\t", reg_feat); |
1560 |
|
|
SMART_PRINTREG("\tsector count register:\t", reg_seccnt); |
1561 |
|
|
SMART_PRINTREG("\tLBA Low register:\t", reg_lbalo); |
1562 |
|
|
SMART_PRINTREG("\tLBA Mid register:\t", reg_lbamid); |
1563 |
|
|
SMART_PRINTREG("\tLBA High register:\t", reg_lbahi); |
1564 |
|
|
SMART_PRINTREG("\tdevice register:\t", reg_dev); |
1565 |
|
|
SMART_PRINTREG("\tcommand register:\t", reg_cmd); |
1566 |
|
|
printf("\ttimestamp:\t\t" |
1567 |
|
|
"%d\t%d\t%d\t%d\t%d\n", |
1568 |
|
|
MAKEDWORD(data->cmd[0].time1, data->cmd[0].time2, |
1569 |
|
|
data->cmd[0].time3, data->cmd[0].time4), |
1570 |
|
|
MAKEDWORD(data->cmd[1].time1, data->cmd[1].time2, |
1571 |
|
|
data->cmd[1].time3, data->cmd[1].time4), |
1572 |
|
|
MAKEDWORD(data->cmd[2].time1, data->cmd[2].time2, |
1573 |
|
|
data->cmd[2].time3, data->cmd[2].time4), |
1574 |
|
|
MAKEDWORD(data->cmd[3].time1, data->cmd[3].time2, |
1575 |
|
|
data->cmd[3].time3, data->cmd[3].time4), |
1576 |
|
|
MAKEDWORD(data->cmd[4].time1, data->cmd[4].time2, |
1577 |
|
|
data->cmd[4].time3, data->cmd[4].time4)); |
1578 |
|
|
} |
1579 |
|
|
|
1580 |
|
|
int |
1581 |
|
|
smart_cksum(u_int8_t *data, size_t len) |
1582 |
|
|
{ |
1583 |
|
|
u_int8_t sum = 0; |
1584 |
|
|
size_t i; |
1585 |
|
|
|
1586 |
|
|
for (i = 0; i < len; i++) |
1587 |
|
|
sum += data[i]; |
1588 |
|
|
|
1589 |
|
|
return (sum); |
1590 |
|
|
} |
1591 |
|
|
|
1592 |
|
|
/* |
1593 |
|
|
* Read device attributes |
1594 |
|
|
*/ |
1595 |
|
|
void |
1596 |
|
|
device_attr(int argc, char *argv[]) |
1597 |
|
|
{ |
1598 |
|
|
struct atareq req; |
1599 |
|
|
struct smart_read attr_val; |
1600 |
|
|
struct smart_threshold attr_thr; |
1601 |
|
|
struct attribute *attr; |
1602 |
|
|
struct threshold *thr; |
1603 |
|
|
const char *attr_name; |
1604 |
|
|
static const char hex[]="0123456789abcdef"; |
1605 |
|
|
char raw[13], *format; |
1606 |
|
|
int i, k, threshold_exceeded = 0; |
1607 |
|
|
|
1608 |
|
|
if (argc != 1) |
1609 |
|
|
goto usage; |
1610 |
|
|
|
1611 |
|
|
memset(&req, 0, sizeof(req)); |
1612 |
|
|
memset(&attr_val, 0, sizeof(attr_val)); /* XXX */ |
1613 |
|
|
memset(&attr_thr, 0, sizeof(attr_thr)); /* XXX */ |
1614 |
|
|
|
1615 |
|
|
req.command = ATAPI_SMART; |
1616 |
|
|
req.cylinder = 0xc24f; /* LBA High = C2h, LBA Mid = 4Fh */ |
1617 |
|
|
req.timeout = 1000; |
1618 |
|
|
|
1619 |
|
|
req.features = ATA_SMART_READ; |
1620 |
|
|
req.flags = ATACMD_READ; |
1621 |
|
|
req.databuf = (caddr_t)&attr_val; |
1622 |
|
|
req.datalen = sizeof(attr_val); |
1623 |
|
|
ata_command(&req); |
1624 |
|
|
|
1625 |
|
|
req.features = ATA_SMART_THRESHOLD; |
1626 |
|
|
req.flags = ATACMD_READ; |
1627 |
|
|
req.databuf = (caddr_t)&attr_thr; |
1628 |
|
|
req.datalen = sizeof(attr_thr); |
1629 |
|
|
ata_command(&req); |
1630 |
|
|
|
1631 |
|
|
if (attr_val.revision != attr_thr.revision) { |
1632 |
|
|
/* |
1633 |
|
|
* Non standard vendor implementation. |
1634 |
|
|
* Return, since we don't know how to use this. |
1635 |
|
|
*/ |
1636 |
|
|
return; |
1637 |
|
|
} |
1638 |
|
|
|
1639 |
|
|
attr = attr_val.attribute; |
1640 |
|
|
thr = attr_thr.threshold; |
1641 |
|
|
|
1642 |
|
|
printf("Attributes table revision: %d\n", attr_val.revision); |
1643 |
|
|
printf("ID\tAttribute name\t\t\tThreshold\tValue\tRaw\n"); |
1644 |
|
|
for (i = 0; i < 30; i++) { |
1645 |
|
|
if (thr[i].id != 0 && thr[i].id == attr[i].id) { |
1646 |
|
|
attr_name = valtostr(thr[i].id, ibm_attr_names); |
1647 |
|
|
if (attr_name == NULL) |
1648 |
|
|
attr_name = "Unknown"; |
1649 |
|
|
|
1650 |
|
|
for (k = 0; k < 6; k++) { |
1651 |
|
|
u_int8_t b; |
1652 |
|
|
b = attr[i].raw[6 - k]; |
1653 |
|
|
raw[k + k] = hex[b >> 4]; |
1654 |
|
|
raw[k + k + 1] = hex[b & 0x0f]; |
1655 |
|
|
} |
1656 |
|
|
raw[k + k] = '\0'; |
1657 |
|
|
if (thr[i].value >= attr[i].value) { |
1658 |
|
|
++threshold_exceeded; |
1659 |
|
|
format = "%3d *%-32.32s %3d\t\t%3d\t0x%s\n"; |
1660 |
|
|
} else { |
1661 |
|
|
format = "%3d\t%-32.32s %3d\t\t%3d\t0x%s\n"; |
1662 |
|
|
} |
1663 |
|
|
printf(format, thr[i].id, attr_name, |
1664 |
|
|
thr[i].value, attr[i].value, raw); |
1665 |
|
|
} |
1666 |
|
|
} |
1667 |
|
|
if (threshold_exceeded) |
1668 |
|
|
fprintf(stderr, "One or more threshold values exceeded!\n"); |
1669 |
|
|
|
1670 |
|
|
return; |
1671 |
|
|
|
1672 |
|
|
usage: |
1673 |
|
|
fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]); |
1674 |
|
|
exit(1); |
1675 |
|
|
} |
1676 |
|
|
|
1677 |
|
|
/* |
1678 |
|
|
* Set the automatic acoustic management on the disk. |
1679 |
|
|
*/ |
1680 |
|
|
void |
1681 |
|
|
device_acoustic(int argc, char *argv[]) |
1682 |
|
|
{ |
1683 |
|
|
u_char acoustic; |
1684 |
|
|
struct atareq req; |
1685 |
|
|
const char *errstr; |
1686 |
|
|
|
1687 |
|
|
if (argc != 2) |
1688 |
|
|
goto usage; |
1689 |
|
|
|
1690 |
|
|
acoustic = strtonum(argv[1], 0, 126, &errstr); |
1691 |
|
|
if (errstr) |
1692 |
|
|
errx(1, "Acoustic management value \"%s\" is %s " |
1693 |
|
|
"(valid values: 0 - 126)", argv[1], errstr); |
1694 |
|
|
|
1695 |
|
|
memset(&req, 0, sizeof(req)); |
1696 |
|
|
|
1697 |
|
|
req.sec_count = acoustic + 0x80; |
1698 |
|
|
|
1699 |
|
|
req.command = SET_FEATURES ; |
1700 |
|
|
req.features = WDSF_AAM_EN ; |
1701 |
|
|
req.timeout = 1000; |
1702 |
|
|
|
1703 |
|
|
ata_command(&req); |
1704 |
|
|
|
1705 |
|
|
return; |
1706 |
|
|
|
1707 |
|
|
usage: |
1708 |
|
|
fprintf(stderr, "usage: %s device %s acoustic-management-level\n", |
1709 |
|
|
__progname, argv[0]); |
1710 |
|
|
exit(1); |
1711 |
|
|
} |
1712 |
|
|
|
1713 |
|
|
/* |
1714 |
|
|
* Set the advanced power managmement on the disk. Power management |
1715 |
|
|
* levels are translated from user-range 0-253 to ATAPI levels 1-0xFD |
1716 |
|
|
* to keep a uniform interface to the user. |
1717 |
|
|
*/ |
1718 |
|
|
void |
1719 |
|
|
device_apm(int argc, char *argv[]) |
1720 |
|
|
{ |
1721 |
|
|
u_char power; |
1722 |
|
|
struct atareq req; |
1723 |
|
|
const char *errstr; |
1724 |
|
|
|
1725 |
|
|
if (argc != 2) |
1726 |
|
|
goto usage; |
1727 |
|
|
|
1728 |
|
|
power = strtonum(argv[1], 0, 253, &errstr); |
1729 |
|
|
if (errstr) |
1730 |
|
|
errx(1, "Advanced power management value \"%s\" is %s " |
1731 |
|
|
"(valid values: 0 - 253)", argv[1], errstr); |
1732 |
|
|
|
1733 |
|
|
memset(&req, 0, sizeof(req)); |
1734 |
|
|
|
1735 |
|
|
req.sec_count = power + 0x01; |
1736 |
|
|
|
1737 |
|
|
req.command = SET_FEATURES ; |
1738 |
|
|
req.features = WDSF_APM_EN ; |
1739 |
|
|
req.timeout = 1000; |
1740 |
|
|
|
1741 |
|
|
ata_command(&req); |
1742 |
|
|
|
1743 |
|
|
return; |
1744 |
|
|
|
1745 |
|
|
usage: |
1746 |
|
|
fprintf(stderr, "usage: %s device %s power-management-level\n", |
1747 |
|
|
__progname, argv[0]); |
1748 |
|
|
exit(1); |
1749 |
|
|
} |
1750 |
|
|
|
1751 |
|
|
/* |
1752 |
|
|
* En/disable features (the automatic acoustic managmement, Advanced Power |
1753 |
|
|
* Management) on the disk. |
1754 |
|
|
*/ |
1755 |
|
|
void |
1756 |
|
|
device_feature(int argc, char *argv[]) |
1757 |
|
|
{ |
1758 |
|
|
struct atareq req; |
1759 |
|
|
|
1760 |
|
|
if (argc != 1) |
1761 |
|
|
goto usage; |
1762 |
|
|
|
1763 |
|
|
memset(&req, 0, sizeof(req)); |
1764 |
|
|
|
1765 |
|
|
req.command = SET_FEATURES ; |
1766 |
|
|
|
1767 |
|
|
if (strcmp(argv[0], "acousticdisable") == 0) |
1768 |
|
|
req.features = WDSF_AAM_DS; |
1769 |
|
|
else if (strcmp(argv[0], "readaheadenable") == 0) |
1770 |
|
|
req.features = WDSF_READAHEAD_EN; |
1771 |
|
|
else if (strcmp(argv[0], "readaheaddisable") == 0) |
1772 |
|
|
req.features = WDSF_READAHEAD_DS; |
1773 |
|
|
else if (strcmp(argv[0], "writecacheenable") == 0) |
1774 |
|
|
req.features = WDSF_EN_WR_CACHE; |
1775 |
|
|
else if (strcmp(argv[0], "writecachedisable") == 0) |
1776 |
|
|
req.features = WDSF_WRITE_CACHE_DS; |
1777 |
|
|
else if (strcmp(argv[0], "apmdisable") == 0) |
1778 |
|
|
req.features = WDSF_APM_DS; |
1779 |
|
|
else if (strcmp(argv[0], "podenable") == 0) |
1780 |
|
|
req.features = WDSF_POD_EN; |
1781 |
|
|
else if (strcmp(argv[0], "poddisable") == 0) |
1782 |
|
|
req.features = WDSF_POD_DS; |
1783 |
|
|
else if (strcmp(argv[0], "puisenable") == 0) |
1784 |
|
|
req.features = WDSF_PUIS_EN; |
1785 |
|
|
else if (strcmp(argv[0], "puisdisable") == 0) |
1786 |
|
|
req.features = WDSF_PUIS_DS; |
1787 |
|
|
else if (strcmp(argv[0], "puisspinup") == 0) |
1788 |
|
|
req.features = WDSF_PUIS_SPINUP; |
1789 |
|
|
else |
1790 |
|
|
goto usage; |
1791 |
|
|
|
1792 |
|
|
req.timeout = 1000; |
1793 |
|
|
|
1794 |
|
|
ata_command(&req); |
1795 |
|
|
|
1796 |
|
|
return; |
1797 |
|
|
|
1798 |
|
|
usage: |
1799 |
|
|
fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]); |
1800 |
|
|
exit(1); |
1801 |
|
|
} |
1802 |
|
|
|
1803 |
|
|
/* |
1804 |
|
|
* Set the idle timer on the disk. Set it for either idle mode or |
1805 |
|
|
* standby mode, depending on how we were invoked. |
1806 |
|
|
*/ |
1807 |
|
|
void |
1808 |
|
|
device_setidle(int argc, char *argv[]) |
1809 |
|
|
{ |
1810 |
|
|
unsigned long idle; |
1811 |
|
|
struct atareq req; |
1812 |
|
|
char *end; |
1813 |
|
|
|
1814 |
|
|
if (argc != 2) |
1815 |
|
|
goto usage; |
1816 |
|
|
|
1817 |
|
|
idle = strtoul(argv[1], &end, 0); |
1818 |
|
|
|
1819 |
|
|
if (*end != '\0' || idle > 19800) |
1820 |
|
|
errx(1, "Invalid idle time: \"%s\" " |
1821 |
|
|
"(valid values: 1 - 19800)", argv[1]); |
1822 |
|
|
|
1823 |
|
|
if (idle != 0 && idle < 5) |
1824 |
|
|
errx(1, "Idle timer must be at least 5 seconds"); |
1825 |
|
|
|
1826 |
|
|
memset(&req, 0, sizeof(req)); |
1827 |
|
|
|
1828 |
|
|
if (idle <= 240 * 5) |
1829 |
|
|
req.sec_count = idle / 5; |
1830 |
|
|
else |
1831 |
|
|
req.sec_count = idle / (30 * 60) + 240; |
1832 |
|
|
|
1833 |
|
|
if (strcmp(argv[0], "setstandby") == 0) |
1834 |
|
|
req.command = WDCC_STANDBY; |
1835 |
|
|
else if (strcmp(argv[0], "setidle") == 0) |
1836 |
|
|
req.command = WDCC_IDLE; |
1837 |
|
|
else |
1838 |
|
|
goto usage; |
1839 |
|
|
req.timeout = 1000; |
1840 |
|
|
|
1841 |
|
|
ata_command(&req); |
1842 |
|
|
|
1843 |
|
|
return; |
1844 |
|
|
|
1845 |
|
|
usage: |
1846 |
|
|
fprintf(stderr, "usage: %s device %s %s\n", __progname, argv[0], |
1847 |
|
|
(strcmp(argv[0], "setidle") == 0) ? "idle-timer" : "standby-timer"); |
1848 |
|
|
exit(1); |
1849 |
|
|
} |
1850 |
|
|
|
1851 |
|
|
/* |
1852 |
|
|
* Query the device for the current power mode |
1853 |
|
|
*/ |
1854 |
|
|
void |
1855 |
|
|
device_checkpower(int argc, char *argv[]) |
1856 |
|
|
{ |
1857 |
|
|
struct atareq req; |
1858 |
|
|
|
1859 |
|
|
if (argc != 1) |
1860 |
|
|
goto usage; |
1861 |
|
|
|
1862 |
|
|
memset(&req, 0, sizeof(req)); |
1863 |
|
|
|
1864 |
|
|
req.command = WDCC_CHECK_PWR; |
1865 |
|
|
req.timeout = 1000; |
1866 |
|
|
req.flags = ATACMD_READREG; |
1867 |
|
|
|
1868 |
|
|
ata_command(&req); |
1869 |
|
|
|
1870 |
|
|
printf("Current power status: "); |
1871 |
|
|
|
1872 |
|
|
switch (req.sec_count) { |
1873 |
|
|
case 0x00: |
1874 |
|
|
printf("Standby mode\n"); |
1875 |
|
|
break; |
1876 |
|
|
case 0x80: |
1877 |
|
|
printf("Idle mode\n"); |
1878 |
|
|
break; |
1879 |
|
|
case 0xff: |
1880 |
|
|
printf("Active mode\n"); |
1881 |
|
|
break; |
1882 |
|
|
default: |
1883 |
|
|
printf("Unknown power code (%02x)\n", req.sec_count); |
1884 |
|
|
} |
1885 |
|
|
|
1886 |
|
|
return; |
1887 |
|
|
usage: |
1888 |
|
|
fprintf(stderr, "usage: %s device %s\n", __progname, argv[0]); |
1889 |
|
|
exit(1); |
1890 |
|
|
} |