atactl.c revision 1.29 1 /* $NetBSD: atactl.c,v 1.29 2004/03/28 01:23:15 mycroft Exp $ */
2
3 /*-
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Ken Hornstein.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * atactl(8) - a program to control ATA devices.
41 */
42 #include <sys/cdefs.h>
43
44 #ifndef lint
45 __RCSID("$NetBSD: atactl.c,v 1.29 2004/03/28 01:23:15 mycroft Exp $");
46 #endif
47
48
49 #include <sys/param.h>
50 #include <sys/ioctl.h>
51 #include <err.h>
52 #include <errno.h>
53 #include <fcntl.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58 #include <util.h>
59
60 #include <dev/ata/atareg.h>
61 #include <sys/ataio.h>
62
63 struct command {
64 const char *cmd_name;
65 const char *arg_names;
66 void (*cmd_func)(int, char *[]);
67 };
68
69 struct bitinfo {
70 u_int bitmask;
71 const char *string;
72 };
73
74 int main(int, char *[]);
75 void usage(void);
76 void ata_command(struct atareq *);
77 void print_bitinfo(const char *, const char *, u_int, struct bitinfo *);
78 void print_smart_status(void *, void *);
79 void print_selftest_entry(int, struct ata_smart_selftest *);
80
81 void print_selftest(void *);
82
83 int is_smart(void);
84
85 int fd; /* file descriptor for device */
86 const char *dvname; /* device name */
87 char dvname_store[MAXPATHLEN]; /* for opendisk(3) */
88 const char *cmdname; /* command user issued */
89 const char *argnames; /* helpstring: expected arguments */
90
91 void device_identify(int, char *[]);
92 void device_setidle(int, char *[]);
93 void device_idle(int, char *[]);
94 void device_checkpower(int, char *[]);
95 void device_smart(int, char *[]);
96
97 void smart_temp(struct ata_smart_attr *, uint64_t);
98
99 struct command commands[] = {
100 { "identify", "", device_identify },
101 { "setidle", "idle-timer", device_setidle },
102 { "setstandby", "standby-timer", device_setidle },
103 { "idle", "", device_idle },
104 { "standby", "", device_idle },
105 { "sleep", "", device_idle },
106 { "checkpower", "", device_checkpower },
107 { "smart", "enable|disable|status|selftest-log", device_smart },
108 { NULL, NULL, NULL },
109 };
110
111 /*
112 * Tables containing bitmasks used for error reporting and
113 * device identification.
114 */
115
116 struct bitinfo ata_caps[] = {
117 { WDC_CAP_DMA, "DMA" },
118 { WDC_CAP_LBA, "LBA" },
119 { ATA_CAP_STBY, "ATA standby timer values" },
120 { WDC_CAP_IORDY, "IORDY operation" },
121 { WDC_CAP_IORDY_DSBL, "IORDY disabling" },
122 { 0, NULL },
123 };
124
125 struct bitinfo ata_vers[] = {
126 { WDC_VER_ATA1, "ATA-1" },
127 { WDC_VER_ATA2, "ATA-2" },
128 { WDC_VER_ATA3, "ATA-3" },
129 { WDC_VER_ATA4, "ATA-4" },
130 { WDC_VER_ATA5, "ATA-5" },
131 { WDC_VER_ATA6, "ATA-6" },
132 { WDC_VER_ATA7, "ATA-7" },
133 { 0, NULL },
134 };
135
136 struct bitinfo ata_cmd_set1[] = {
137 { WDC_CMD1_NOP, "NOP command" },
138 { WDC_CMD1_RB, "READ BUFFER command" },
139 { WDC_CMD1_WB, "WRITE BUFFER command" },
140 { WDC_CMD1_HPA, "Host Protected Area feature set" },
141 { WDC_CMD1_DVRST, "DEVICE RESET command" },
142 { WDC_CMD1_SRV, "SERVICE interrupt" },
143 { WDC_CMD1_RLSE, "release interrupt" },
144 { WDC_CMD1_AHEAD, "look-ahead" },
145 { WDC_CMD1_CACHE, "write cache" },
146 { WDC_CMD1_PKT, "PACKET command feature set" },
147 { WDC_CMD1_PM, "Power Management feature set" },
148 { WDC_CMD1_REMOV, "Removable Media feature set" },
149 { WDC_CMD1_SEC, "Security Mode feature set" },
150 { WDC_CMD1_SMART, "SMART feature set" },
151 { 0, NULL },
152 };
153
154 struct bitinfo ata_cmd_set2[] = {
155 { ATA_CMD2_FCE, "FLUSH CACHE EXT command" },
156 { WDC_CMD2_FC, "FLUSH CACHE command" },
157 { WDC_CMD2_DCO, "Device Configuration Overlay feature set" },
158 { ATA_CMD2_LBA48, "48-bit Address feature set" },
159 { WDC_CMD2_AAM, "Automatic Acoustic Management feature set" },
160 { WDC_CMD2_SM, "SET MAX security extension" },
161 { WDC_CMD2_SFREQ, "SET FEATURES required to spin-up after power-up" },
162 { WDC_CMD2_PUIS, "Power-Up In Standby feature set" },
163 { WDC_CMD2_RMSN, "Removable Media Status Notification feature set" },
164 { ATA_CMD2_APM, "Advanced Power Management feature set" },
165 { ATA_CMD2_CFA, "CFA feature set" },
166 { ATA_CMD2_RWQ, "READ/WRITE DMA QUEUED commands" },
167 { WDC_CMD2_DM, "DOWNLOAD MICROCODE command" },
168 { 0, NULL },
169 };
170
171 struct bitinfo ata_cmd_ext[] = {
172 { ATA_CMDE_TLCONT, "Time-limited R/W feature set R/W Continuous mode" },
173 { ATA_CMDE_TL, "Time-limited Read/Write" },
174 { ATA_CMDE_URGW, "URG bit for WRITE STREAM DMA/PIO" },
175 { ATA_CMDE_URGR, "URG bit for READ STREAM DMA/PIO" },
176 { ATA_CMDE_WWN, "World Wide name" },
177 { ATA_CMDE_WQFE, "WRITE DMA QUEUED FUA EXT command" },
178 { ATA_CMDE_WFE, "WRITE DMA/MULTIPLE FUA EXT commands" },
179 { ATA_CMDE_GPL, "General Purpose Logging feature set" },
180 { ATA_CMDE_STREAM, "Streaming feature set" },
181 { ATA_CMDE_MCPTC, "Media Card Pass Through Command feature set" },
182 { ATA_CMDE_MS, "Media serial number" },
183 { ATA_CMDE_SST, "SMART self-test" },
184 { ATA_CMDE_SEL, "SMART error logging" },
185 { 0, NULL },
186 };
187
188 static const struct {
189 const int id;
190 const char *name;
191 void (*special)(struct ata_smart_attr *, uint64_t);
192 } smart_attrs[] = {
193 { 1, "Raw read error rate" },
194 { 2, "Throughput performance" },
195 { 3, "Spin-up time" },
196 { 4, "Start/stop count" },
197 { 5, "Reallocated sector count" },
198 { 7, "Seek error rate" },
199 { 8, "Seek time performance" },
200 { 9, "Power-on hours count" },
201 { 10, "Spin retry count" },
202 { 11, "Calibration retry count" },
203 { 12, "Device power cycle count" },
204 { 191, "Gsense error rate" },
205 { 192, "Power-off retract count" },
206 { 193, "Load cycle count" },
207 { 194, "Temperature", smart_temp},
208 { 195, "Hardware ECC Recovered" },
209 { 196, "Reallocated event count" },
210 { 197, "Current pending sector" },
211 { 198, "Offline uncorrectable" },
212 { 199, "Ultra DMA CRC error count" },
213 { 0, "Unknown" },
214 };
215
216 int
217 main(int argc, char *argv[])
218 {
219 int i;
220
221 /* Must have at least: device command */
222 if (argc < 3)
223 usage();
224
225 /* Skip program name, get and skip device name and command. */
226 dvname = argv[1];
227 cmdname = argv[2];
228 argv += 3;
229 argc -= 3;
230
231 /*
232 * Open the device
233 */
234 fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0);
235 if (fd == -1) {
236 if (errno == ENOENT) {
237 /*
238 * Device doesn't exist. Probably trying to open
239 * a device which doesn't use disk semantics for
240 * device name. Try again, specifying "cooked",
241 * which leaves off the "r" in front of the device's
242 * name.
243 */
244 fd = opendisk(dvname, O_RDWR, dvname_store,
245 sizeof(dvname_store), 1);
246 if (fd == -1)
247 err(1, "%s", dvname);
248 } else
249 err(1, "%s", dvname);
250 }
251
252 /*
253 * Point the dvname at the actual device name that opendisk() opened.
254 */
255 dvname = dvname_store;
256
257 /* Look up and call the command. */
258 for (i = 0; commands[i].cmd_name != NULL; i++)
259 if (strcmp(cmdname, commands[i].cmd_name) == 0)
260 break;
261 if (commands[i].cmd_name == NULL)
262 errx(1, "unknown command: %s", cmdname);
263
264 argnames = commands[i].arg_names;
265
266 (*commands[i].cmd_func)(argc, argv);
267 exit(0);
268 }
269
270 void
271 usage(void)
272 {
273 int i;
274
275 fprintf(stderr, "usage: %s device command [arg [...]]\n",
276 getprogname());
277
278 fprintf(stderr, " Available device commands:\n");
279 for (i=0; commands[i].cmd_name != NULL; i++)
280 fprintf(stderr, "\t%s %s\n", commands[i].cmd_name,
281 commands[i].arg_names);
282
283 exit(1);
284 }
285
286 /*
287 * Wrapper that calls ATAIOCCOMMAND and checks for errors
288 */
289
290 void
291 ata_command(struct atareq *req)
292 {
293 int error;
294
295 error = ioctl(fd, ATAIOCCOMMAND, req);
296
297 if (error == -1)
298 err(1, "ATAIOCCOMMAND failed");
299
300 switch (req->retsts) {
301
302 case ATACMD_OK:
303 return;
304 case ATACMD_TIMEOUT:
305 fprintf(stderr, "ATA command timed out\n");
306 exit(1);
307 case ATACMD_DF:
308 fprintf(stderr, "ATA device returned a Device Fault\n");
309 exit(1);
310 case ATACMD_ERROR:
311 if (req->error & WDCE_ABRT)
312 fprintf(stderr, "ATA device returned Aborted "
313 "Command\n");
314 else
315 fprintf(stderr, "ATA device returned error register "
316 "%0x\n", req->error);
317 exit(1);
318 default:
319 fprintf(stderr, "ATAIOCCOMMAND returned unknown result code "
320 "%d\n", req->retsts);
321 exit(1);
322 }
323 }
324
325 /*
326 * Print out strings associated with particular bitmasks
327 */
328
329 void
330 print_bitinfo(const char *bf, const char *af, u_int bits, struct bitinfo *binfo)
331 {
332
333 for (; binfo->bitmask != 0; binfo++)
334 if (bits & binfo->bitmask)
335 printf("%s%s%s", bf, binfo->string, af);
336 }
337
338
339 /*
340 * Try to print SMART temperature field
341 */
342
343 void
344 smart_temp(struct ata_smart_attr *attr, uint64_t raw_value)
345 {
346 printf("%" PRIu8, attr->raw[0]);
347 if (attr->raw[0] != raw_value)
348 printf(" Lifetime max/min %" PRIu8 "/%" PRIu8,
349 attr->raw[2], attr->raw[4]);
350 }
351
352
353 /*
354 * Print out SMART attribute thresholds and values
355 */
356
357 void
358 print_smart_status(void *vbuf, void *tbuf)
359 {
360 struct ata_smart_attributes *value_buf = vbuf;
361 struct ata_smart_thresholds *threshold_buf = tbuf;
362 struct ata_smart_attr *attr;
363 uint64_t raw_value;
364 int flags;
365 int i, j;
366 int aid;
367 int8_t checksum;
368
369 for (i = checksum = 0; i < 511; i++)
370 checksum += ((int8_t *) value_buf)[i];
371 checksum *= -1;
372 if (checksum != value_buf->checksum) {
373 fprintf(stderr, "SMART attribute values checksum error\n");
374 return;
375 }
376
377 for (i = checksum = 0; i < 511; i++)
378 checksum += ((int8_t *) threshold_buf)[i];
379 checksum *= -1;
380 if (checksum != threshold_buf->checksum) {
381 fprintf(stderr, "SMART attribute thresholds checksum error\n");
382 return;
383 }
384
385 printf("id value thresh crit collect reliability description\t\t\traw\n");
386 for (i = 0; i < 256; i++) {
387 int thresh = 0;
388
389 attr = NULL;
390
391 for (j = 0; j < 30; j++) {
392 if (value_buf->attributes[j].id == i)
393 attr = &value_buf->attributes[j];
394 if (threshold_buf->thresholds[j].id == i)
395 thresh = threshold_buf->thresholds[j].value;
396 }
397
398 if (thresh && attr == NULL)
399 errx(1, "threshold but not attr %d", i);
400 if (attr == NULL)
401 continue;
402
403 if (attr->value == 0||attr->value == 0xFE||attr->value == 0xFF)
404 continue;
405
406 for (aid = 0;
407 smart_attrs[aid].id != i && smart_attrs[aid].id != 0;
408 aid++)
409 ;
410
411 flags = attr->flags;
412
413 printf("%3d %3d %3d %-3s %-7s %stive %-24s\t",
414 i, attr->value, thresh,
415 flags & WDSM_ATTR_ADVISORY ? "yes" : "no",
416 flags & WDSM_ATTR_COLLECTIVE ? "online" : "offline",
417 attr->value > thresh ? "posi" : "nega",
418 smart_attrs[aid].name);
419
420 for (j = 0, raw_value = 0; j < 6; j++)
421 raw_value += ((uint64_t)attr->raw[j]) << (8*j);
422
423 if (smart_attrs[aid].special)
424 (*smart_attrs[aid].special)(attr, raw_value);
425 else
426 printf("%" PRIu64, raw_value);
427 printf("\n");
428 }
429 }
430
431 struct {
432 int number;
433 const char *name;
434 } selftest_name[] = {
435 { 0, "Off-line" },
436 { 1, "Short off-line" },
437 { 2, "Extended off-line" },
438 { 127, "Abort off-line test" },
439 { 129, "Short captive" },
440 { 130, "Extended captive" },
441 { 256, "Unknown test" }, /* larger then u_int8_t */
442 { 0, NULL }
443 };
444
445 const char *selftest_status[] = {
446 "No error",
447 "Aborted by the host",
448 "Interruped by the host by reset",
449 "Fatal error or unknown test error",
450 "Unknown test element failed",
451 "Electrical test element failed",
452 "The Servo (and/or seek) test element failed",
453 "Read element of test failed",
454 "Reserved",
455 "Reserved",
456 "Reserved",
457 "Reserved",
458 "Reserved",
459 "Reserved",
460 "Reserved",
461 "Self-test in progress"
462 };
463
464 void
465 print_selftest_entry(int num, struct ata_smart_selftest *le)
466 {
467 unsigned char *p;
468 int i;
469
470 /* check if all zero */
471 for (p = (void *)le, i = 0; i < sizeof(*le); i++)
472 if (p[i] != 0)
473 break;
474 if (i == sizeof(*le))
475 return;
476
477 printf("Log entry: %d\n", num);
478
479 /* Get test name */
480 for (i = 0; selftest_name[i].name != NULL; i++)
481 if (selftest_name[i].number == le->number)
482 break;
483 if (selftest_name[i].number == 0)
484 i = 255; /* unknown test */
485
486 printf("\tName: %s\n", selftest_name[i].name);
487 printf("\tStatus: %s\n", selftest_status[le->status >> 4]);
488 if (le->status >> 4 == 15)
489 printf("\tPrecent of test remaning: %1d0\n", le->status & 0xf);
490 if (le->status)
491 printf("LBA first error: %d\n", le->lba_first_error);
492 }
493
494 void
495 print_selftest(void *buf)
496 {
497 struct ata_smart_selftestlog *stlog = buf;
498 int8_t checksum;
499 int i;
500
501 for (i = checksum = 0; i < 511; i++)
502 checksum += ((int8_t *) buf)[i];
503 checksum *= -1;
504 if ((u_int8_t)checksum != stlog->checksum) {
505 fprintf(stderr, "SMART selftest log checksum error\n");
506 return;
507 }
508
509 if (stlog->data_structure_revision != 1) {
510 fprintf(stderr, "Log revision not 1");
511 return;
512 }
513
514 if (stlog->mostrecenttest == 0) {
515 printf("No self-tests have been logged\n");
516 return;
517 }
518
519 if (stlog->mostrecenttest > 22) {
520 fprintf(stderr, "Most recent test is too large\n");
521 return;
522 }
523
524 for (i = stlog->mostrecenttest; i < 22; i++)
525 print_selftest_entry(i, &stlog->log_entries[i]);
526 for (i = 0; i < stlog->mostrecenttest; i++)
527 print_selftest_entry(i, &stlog->log_entries[i]);
528 }
529
530 /*
531 * is_smart:
532 *
533 * Detect whether device supports SMART and SMART is enabled.
534 */
535
536 int
537 is_smart(void)
538 {
539 int retval = 0;
540 struct atareq req;
541 unsigned char inbuf[DEV_BSIZE];
542 struct ataparams *inqbuf;
543 char *status;
544
545 memset(&inbuf, 0, sizeof(inbuf));
546 memset(&req, 0, sizeof(req));
547
548 inqbuf = (struct ataparams *) inbuf;
549
550 req.flags = ATACMD_READ;
551 req.command = WDCC_IDENTIFY;
552 req.databuf = (caddr_t) inbuf;
553 req.datalen = sizeof(inbuf);
554 req.timeout = 1000;
555
556 ata_command(&req);
557
558 if (inqbuf->atap_cmd_def != 0 && inqbuf->atap_cmd_def != 0xffff) {
559 if (!(inqbuf->atap_cmd_set1 & WDC_CMD1_SMART)) {
560 fprintf(stderr, "SMART unsupported\n");
561 } else {
562 if (inqbuf->atap_ata_major <= WDC_VER_ATA5 ||
563 inqbuf->atap_cmd_set2 == 0xffff ||
564 inqbuf->atap_cmd_set2 == 0x0000) {
565 status = "status unknown";
566 retval = 2;
567 } else {
568 if (inqbuf->atap_cmd1_en & WDC_CMD1_SMART) {
569 status = "enabled";
570 retval = 1;
571 } else {
572 status = "disabled";
573 }
574 }
575 printf("SMART supported, SMART %s\n", status);
576 }
577 }
578 return retval;
579 }
580
581 /*
582 * DEVICE COMMANDS
583 */
584
585 /*
586 * device_identify:
587 *
588 * Display the identity of the device
589 */
590 void
591 device_identify(int argc, char *argv[])
592 {
593 struct ataparams *inqbuf;
594 struct atareq req;
595 unsigned char inbuf[DEV_BSIZE];
596 #if BYTE_ORDER == LITTLE_ENDIAN
597 int i;
598 u_int16_t *p;
599 #endif
600
601 /* No arguments. */
602 if (argc != 0)
603 usage();
604
605 memset(&inbuf, 0, sizeof(inbuf));
606 memset(&req, 0, sizeof(req));
607
608 inqbuf = (struct ataparams *) inbuf;
609
610 req.flags = ATACMD_READ;
611 req.command = WDCC_IDENTIFY;
612 req.databuf = (caddr_t) inbuf;
613 req.datalen = sizeof(inbuf);
614 req.timeout = 1000;
615
616 ata_command(&req);
617
618 #if BYTE_ORDER == LITTLE_ENDIAN
619 /*
620 * On little endian machines, we need to shuffle the string
621 * byte order. However, we don't have to do this for NEC or
622 * Mitsumi ATAPI devices
623 */
624
625 if (!((inqbuf->atap_config & WDC_CFG_ATAPI_MASK) == WDC_CFG_ATAPI &&
626 ((inqbuf->atap_model[0] == 'N' &&
627 inqbuf->atap_model[1] == 'E') ||
628 (inqbuf->atap_model[0] == 'F' &&
629 inqbuf->atap_model[1] == 'X')))) {
630 for (i = 0 ; i < sizeof(inqbuf->atap_model); i += 2) {
631 p = (u_short *) (inqbuf->atap_model + i);
632 *p = ntohs(*p);
633 }
634 for (i = 0 ; i < sizeof(inqbuf->atap_serial); i += 2) {
635 p = (u_short *) (inqbuf->atap_serial + i);
636 *p = ntohs(*p);
637 }
638 for (i = 0 ; i < sizeof(inqbuf->atap_revision); i += 2) {
639 p = (u_short *) (inqbuf->atap_revision + i);
640 *p = ntohs(*p);
641 }
642 }
643 #endif
644
645 /*
646 * Strip blanks off of the info strings. Yuck, I wish this was
647 * cleaner.
648 */
649
650 if (inqbuf->atap_model[sizeof(inqbuf->atap_model) - 1] == ' ') {
651 inqbuf->atap_model[sizeof(inqbuf->atap_model) - 1] = '\0';
652 while (inqbuf->atap_model[strlen(inqbuf->atap_model) - 1] == ' ')
653 inqbuf->atap_model[strlen(inqbuf->atap_model) - 1] = '\0';
654 }
655
656 if (inqbuf->atap_revision[sizeof(inqbuf->atap_revision) - 1] == ' ') {
657 inqbuf->atap_revision[sizeof(inqbuf->atap_revision) - 1] = '\0';
658 while (inqbuf->atap_revision[strlen(inqbuf->atap_revision) - 1] == ' ')
659 inqbuf->atap_revision[strlen(inqbuf->atap_revision) - 1] = '\0';
660 }
661
662 if (inqbuf->atap_serial[sizeof(inqbuf->atap_serial) - 1] == ' ') {
663 inqbuf->atap_serial[sizeof(inqbuf->atap_serial) - 1] = '\0';
664 while (inqbuf->atap_serial[strlen(inqbuf->atap_serial) - 1] == ' ')
665 inqbuf->atap_serial[strlen(inqbuf->atap_serial) - 1] = '\0';
666 }
667
668 printf("Model: %.*s, Rev: %.*s, Serial #: %.*s\n",
669 (int) sizeof(inqbuf->atap_model), inqbuf->atap_model,
670 (int) sizeof(inqbuf->atap_revision), inqbuf->atap_revision,
671 (int) sizeof(inqbuf->atap_serial), inqbuf->atap_serial);
672
673 printf("Device type: %s, %s\n", inqbuf->atap_config & WDC_CFG_ATAPI ?
674 "ATAPI" : "ATA", inqbuf->atap_config & ATA_CFG_FIXED ? "fixed" :
675 "removable");
676
677 if ((inqbuf->atap_config & WDC_CFG_ATAPI_MASK) == 0)
678 printf("Cylinders: %d, heads: %d, sec/track: %d, total "
679 "sectors: %d\n", inqbuf->atap_cylinders,
680 inqbuf->atap_heads, inqbuf->atap_sectors,
681 (inqbuf->atap_capacity[1] << 16) |
682 inqbuf->atap_capacity[0]);
683
684 if (inqbuf->atap_queuedepth & WDC_QUEUE_DEPTH_MASK)
685 printf("Device supports command queue depth of %d\n",
686 inqbuf->atap_queuedepth & 0xf);
687
688 printf("Device capabilities:\n");
689 print_bitinfo("\t", "\n", inqbuf->atap_capabilities1, ata_caps);
690
691 if (inqbuf->atap_ata_major != 0 && inqbuf->atap_ata_major != 0xffff) {
692 printf("Device supports following standards:\n");
693 print_bitinfo("", " ", inqbuf->atap_ata_major, ata_vers);
694 printf("\n");
695 }
696
697 if (inqbuf->atap_cmd_set1 != 0 && inqbuf->atap_cmd_set1 != 0xffff &&
698 inqbuf->atap_cmd_set2 != 0 && inqbuf->atap_cmd_set2 != 0xffff) {
699 printf("Command set support:\n");
700 print_bitinfo("\t", "\n", inqbuf->atap_cmd_set1, ata_cmd_set1);
701 print_bitinfo("\t", "\n", inqbuf->atap_cmd_set2, ata_cmd_set2);
702 if (inqbuf->atap_cmd_ext != 0 && inqbuf->atap_cmd_ext != 0xffff)
703 print_bitinfo("\t", "\n", inqbuf->atap_cmd_ext,
704 ata_cmd_ext);
705 }
706
707 if (inqbuf->atap_cmd_def != 0 && inqbuf->atap_cmd_def != 0xffff) {
708 printf("Command sets/features enabled:\n");
709 print_bitinfo("\t", "\n", inqbuf->atap_cmd1_en &
710 (WDC_CMD1_SRV | WDC_CMD1_RLSE | WDC_CMD1_AHEAD |
711 WDC_CMD1_CACHE | WDC_CMD1_SEC | WDC_CMD1_SMART),
712 ata_cmd_set1);
713 print_bitinfo("\t", "\n", inqbuf->atap_cmd2_en &
714 (WDC_CMD2_RMSN | ATA_CMD2_APM), ata_cmd_set2);
715 }
716
717 return;
718 }
719
720 /*
721 * device idle:
722 *
723 * issue the IDLE IMMEDIATE command to the drive
724 */
725
726 void
727 device_idle(int argc, char *argv[])
728 {
729 struct atareq req;
730
731 /* No arguments. */
732 if (argc != 0)
733 usage();
734
735 memset(&req, 0, sizeof(req));
736
737 if (strcmp(cmdname, "idle") == 0)
738 req.command = WDCC_IDLE_IMMED;
739 else if (strcmp(cmdname, "standby") == 0)
740 req.command = WDCC_STANDBY_IMMED;
741 else
742 req.command = WDCC_SLEEP;
743
744 req.timeout = 1000;
745
746 ata_command(&req);
747
748 return;
749 }
750
751 /*
752 * Set the idle timer on the disk. Set it for either idle mode or
753 * standby mode, depending on how we were invoked.
754 */
755
756 void
757 device_setidle(int argc, char *argv[])
758 {
759 unsigned long idle;
760 struct atareq req;
761 char *end;
762
763 /* Only one argument */
764 if (argc != 1)
765 usage();
766
767 idle = strtoul(argv[0], &end, 0);
768
769 if (*end != '\0') {
770 fprintf(stderr, "Invalid idle time: \"%s\"\n", argv[0]);
771 exit(1);
772 }
773
774 if (idle > 19800) {
775 fprintf(stderr, "Idle time has a maximum value of 5.5 "
776 "hours\n");
777 exit(1);
778 }
779
780 if (idle != 0 && idle < 5) {
781 fprintf(stderr, "Idle timer must be at least 5 seconds\n");
782 exit(1);
783 }
784
785 memset(&req, 0, sizeof(req));
786
787 if (idle <= 240*5)
788 req.sec_count = idle / 5;
789 else
790 req.sec_count = idle / (30*60) + 240;
791
792 req.command = cmdname[3] == 's' ? WDCC_STANDBY : WDCC_IDLE;
793 req.timeout = 1000;
794
795 ata_command(&req);
796
797 return;
798 }
799
800 /*
801 * Query the device for the current power mode
802 */
803
804 void
805 device_checkpower(int argc, char *argv[])
806 {
807 struct atareq req;
808
809 /* No arguments. */
810 if (argc != 0)
811 usage();
812
813 memset(&req, 0, sizeof(req));
814
815 req.command = WDCC_CHECK_PWR;
816 req.timeout = 1000;
817 req.flags = ATACMD_READREG;
818
819 ata_command(&req);
820
821 printf("Current power status: ");
822
823 switch (req.sec_count) {
824 case 0x00:
825 printf("Standby mode\n");
826 break;
827 case 0x80:
828 printf("Idle mode\n");
829 break;
830 case 0xff:
831 printf("Active mode\n");
832 break;
833 default:
834 printf("Unknown power code (%02x)\n", req.sec_count);
835 }
836
837 return;
838 }
839
840 /*
841 * device_smart:
842 *
843 * Display SMART status
844 */
845 void
846 device_smart(int argc, char *argv[])
847 {
848 struct atareq req;
849 unsigned char inbuf[DEV_BSIZE];
850 unsigned char inbuf2[DEV_BSIZE];
851
852 /* Only one argument */
853 if (argc != 1)
854 usage();
855
856 if (strcmp(argv[0], "enable") == 0) {
857 memset(&req, 0, sizeof(req));
858
859 req.features = WDSM_ENABLE_OPS;
860 req.command = WDCC_SMART;
861 req.cylinder = htole16(WDSMART_CYL);
862 req.timeout = 1000;
863
864 ata_command(&req);
865
866 is_smart();
867 } else if (strcmp(argv[0], "disable") == 0) {
868 memset(&req, 0, sizeof(req));
869
870 req.features = WDSM_DISABLE_OPS;
871 req.command = WDCC_SMART;
872 req.cylinder = htole16(WDSMART_CYL);
873 req.timeout = 1000;
874
875 ata_command(&req);
876
877 is_smart();
878 } else if (strcmp(argv[0], "status") == 0) {
879 if (!is_smart()) {
880 fprintf(stderr, "SMART not supported\n");
881 return;
882 }
883
884 memset(&inbuf, 0, sizeof(inbuf));
885 memset(&req, 0, sizeof(req));
886
887 req.features = WDSM_STATUS;
888 req.command = WDCC_SMART;
889 req.cylinder = htole16(WDSMART_CYL);
890 req.timeout = 1000;
891
892 ata_command(&req);
893
894 if (req.cylinder != htole16(WDSMART_CYL)) {
895 fprintf(stderr, "Threshold exceeds condition\n");
896 }
897
898 /* WDSM_RD_DATA and WDSM_RD_THRESHOLDS are optional
899 * features, the following ata_command()'s may error
900 * and exit().
901 */
902
903 memset(&inbuf, 0, sizeof(inbuf));
904 memset(&req, 0, sizeof(req));
905
906 req.flags = ATACMD_READ;
907 req.features = WDSM_RD_DATA;
908 req.command = WDCC_SMART;
909 req.databuf = (caddr_t) inbuf;
910 req.datalen = sizeof(inbuf);
911 req.cylinder = htole16(WDSMART_CYL);
912 req.timeout = 1000;
913
914 ata_command(&req);
915
916 memset(&inbuf2, 0, sizeof(inbuf2));
917 memset(&req, 0, sizeof(req));
918
919 req.flags = ATACMD_READ;
920 req.features = WDSM_RD_THRESHOLDS;
921 req.command = WDCC_SMART;
922 req.databuf = (caddr_t) inbuf2;
923 req.datalen = sizeof(inbuf2);
924 req.cylinder = htole16(WDSMART_CYL);
925 req.timeout = 1000;
926
927 ata_command(&req);
928
929 print_smart_status(inbuf, inbuf2);
930
931 } else if (strcmp(argv[0], "selftest-log") == 0) {
932 if (!is_smart()) {
933 fprintf(stderr, "SMART not supported\n");
934 return;
935 }
936
937 memset(&inbuf, 0, sizeof(inbuf));
938 memset(&req, 0, sizeof(req));
939
940 req.flags = ATACMD_READ;
941 req.features = WDSM_RD_LOG;
942 req.sec_count = 1;
943 req.sec_num = 6;
944 req.command = WDCC_SMART;
945 req.databuf = (caddr_t) inbuf;
946 req.datalen = sizeof(inbuf);
947 req.cylinder = htole16(WDSMART_CYL);
948 req.timeout = 1000;
949
950 ata_command(&req);
951
952 print_selftest(inbuf);
953
954 } else {
955 usage();
956 }
957 return;
958 }
959