1 /* $NetBSD: atactl.c,v 1.21 2003/06/23 11:53:35 agc 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.21 2003/06/23 11:53:35 agc 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 <dev/ata/atavar.h> 62 #include <dev/ic/wdcreg.h> 63 #include <sys/ataio.h> 64 65 struct command { 66 const char *cmd_name; 67 const char *arg_names; 68 void (*cmd_func)(int, char *[]); 69 }; 70 71 struct bitinfo { 72 u_int bitmask; 73 const char *string; 74 }; 75 76 int main(int, char *[]); 77 void usage(void); 78 void ata_command(struct atareq *); 79 void print_bitinfo(const char *, const char *, u_int, struct bitinfo *); 80 void print_smart_status(void *vbuf, void *tbuf); 81 int is_smart(void); 82 83 int fd; /* file descriptor for device */ 84 const char *dvname; /* device name */ 85 char dvname_store[MAXPATHLEN]; /* for opendisk(3) */ 86 const char *cmdname; /* command user issued */ 87 const char *argnames; /* helpstring: expected arguments */ 88 89 void device_identify(int, char *[]); 90 void device_setidle(int, char *[]); 91 void device_idle(int, char *[]); 92 void device_checkpower(int, char *[]); 93 void device_smart(int, char *[]); 94 95 struct command commands[] = { 96 { "identify", "", device_identify }, 97 { "setidle", "idle-timer", device_setidle }, 98 { "setstandby", "standby-timer", device_setidle }, 99 { "idle", "", device_idle }, 100 { "standby", "", device_idle }, 101 { "sleep", "", device_idle }, 102 { "checkpower", "", device_checkpower }, 103 { "smart", "enable|disable|status", device_smart }, 104 { NULL, NULL, NULL }, 105 }; 106 107 /* 108 * Tables containing bitmasks used for error reporting and 109 * device identification. 110 */ 111 112 struct bitinfo ata_caps[] = { 113 { ATA_CAP_STBY, "ATA standby timer values" }, 114 { WDC_CAP_IORDY, "IORDY operation" }, 115 { WDC_CAP_IORDY_DSBL, "IORDY disabling" }, 116 { NULL, NULL }, 117 }; 118 119 struct bitinfo ata_vers[] = { 120 { WDC_VER_ATA1, "ATA-1" }, 121 { WDC_VER_ATA2, "ATA-2" }, 122 { WDC_VER_ATA3, "ATA-3" }, 123 { WDC_VER_ATA4, "ATA-4" }, 124 { NULL, NULL }, 125 }; 126 127 struct bitinfo ata_cmd_set1[] = { 128 { WDC_CMD1_NOP, "NOP command" }, 129 { WDC_CMD1_RB, "READ BUFFER command" }, 130 { WDC_CMD1_WB, "WRITE BUFFER command" }, 131 { WDC_CMD1_HPA, "Host Protected Area feature set" }, 132 { WDC_CMD1_DVRST, "DEVICE RESET command" }, 133 { WDC_CMD1_SRV, "SERVICE interrupt" }, 134 { WDC_CMD1_RLSE, "release interrupt" }, 135 { WDC_CMD1_AHEAD, "look-ahead" }, 136 { WDC_CMD1_CACHE, "write cache" }, 137 { WDC_CMD1_PKT, "PACKET command feature set" }, 138 { WDC_CMD1_PM, "Power Management feature set" }, 139 { WDC_CMD1_REMOV, "Removable Media feature set" }, 140 { WDC_CMD1_SEC, "Security Mode feature set" }, 141 { WDC_CMD1_SMART, "SMART feature set" }, 142 { NULL, NULL }, 143 }; 144 145 struct bitinfo ata_cmd_set2[] = { 146 { WDC_CMD2_RMSN, "Removable Media Status Notification feature set" }, 147 { ATA_CMD2_APM, "Advanced Power Management feature set" }, 148 { ATA_CMD2_CFA, "CFA feature set" }, 149 { ATA_CMD2_RWQ, "READ/WRITE DMA QUEUED commands" }, 150 { WDC_CMD2_DM, "DOWNLOAD MICROCODE command" }, 151 { NULL, NULL }, 152 }; 153 154 static const struct { 155 const int id; 156 const char *name; 157 } smart_attrs[] = { 158 { 1, "Raw read error rate" }, 159 { 2, "Throughput performance" }, 160 { 3, "Spin-up time" }, 161 { 4, "Start/stop count" }, 162 { 5, "Reallocated sector count" }, 163 { 7, "Seek error rate" }, 164 { 8, "Seek time performance" }, 165 { 9, "Power-on hours count" }, 166 { 10, "Spin retry count" }, 167 { 11, "Calibration retry count" }, 168 { 12, "Device power cycle count" }, 169 { 191, "Gsense error rate" }, 170 { 192, "Power-off retract count" }, 171 { 193, "Load cycle count" }, 172 { 194, "Temperature" }, 173 { 196, "Reallocated event count" }, 174 { 197, "Current pending sector" }, 175 { 198, "Offline uncorrectable" }, 176 { 199, "Ultra DMA CRC error count" }, 177 { 0, "" }, 178 }; 179 180 int 181 main(int argc, char *argv[]) 182 { 183 int i; 184 185 /* Must have at least: device command */ 186 if (argc < 3) 187 usage(); 188 189 /* Skip program name, get and skip device name and command. */ 190 dvname = argv[1]; 191 cmdname = argv[2]; 192 argv += 3; 193 argc -= 3; 194 195 /* 196 * Open the device 197 */ 198 fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0); 199 if (fd == -1) { 200 if (errno == ENOENT) { 201 /* 202 * Device doesn't exist. Probably trying to open 203 * a device which doesn't use disk semantics for 204 * device name. Try again, specifying "cooked", 205 * which leaves off the "r" in front of the device's 206 * name. 207 */ 208 fd = opendisk(dvname, O_RDWR, dvname_store, 209 sizeof(dvname_store), 1); 210 if (fd == -1) 211 err(1, "%s", dvname); 212 } else 213 err(1, "%s", dvname); 214 } 215 216 /* 217 * Point the dvname at the actual device name that opendisk() opened. 218 */ 219 dvname = dvname_store; 220 221 /* Look up and call the command. */ 222 for (i = 0; commands[i].cmd_name != NULL; i++) 223 if (strcmp(cmdname, commands[i].cmd_name) == 0) 224 break; 225 if (commands[i].cmd_name == NULL) 226 errx(1, "unknown command: %s", cmdname); 227 228 argnames = commands[i].arg_names; 229 230 (*commands[i].cmd_func)(argc, argv); 231 exit(0); 232 } 233 234 void 235 usage(void) 236 { 237 int i; 238 239 fprintf(stderr, "Usage: %s device command [arg [...]]\n", 240 getprogname()); 241 242 fprintf(stderr, " Available device commands:\n"); 243 for (i=0; commands[i].cmd_name != NULL; i++) 244 fprintf(stderr, "\t%s %s\n", commands[i].cmd_name, 245 commands[i].arg_names); 246 247 exit(1); 248 } 249 250 /* 251 * Wrapper that calls ATAIOCCOMMAND and checks for errors 252 */ 253 254 void 255 ata_command(struct atareq *req) 256 { 257 int error; 258 259 error = ioctl(fd, ATAIOCCOMMAND, req); 260 261 if (error == -1) 262 err(1, "ATAIOCCOMMAND failed"); 263 264 switch (req->retsts) { 265 266 case ATACMD_OK: 267 return; 268 case ATACMD_TIMEOUT: 269 fprintf(stderr, "ATA command timed out\n"); 270 exit(1); 271 case ATACMD_DF: 272 fprintf(stderr, "ATA device returned a Device Fault\n"); 273 exit(1); 274 case ATACMD_ERROR: 275 if (req->error & WDCE_ABRT) 276 fprintf(stderr, "ATA device returned Aborted " 277 "Command\n"); 278 else 279 fprintf(stderr, "ATA device returned error register " 280 "%0x\n", req->error); 281 exit(1); 282 default: 283 fprintf(stderr, "ATAIOCCOMMAND returned unknown result code " 284 "%d\n", req->retsts); 285 exit(1); 286 } 287 } 288 289 /* 290 * Print out strings associated with particular bitmasks 291 */ 292 293 void 294 print_bitinfo(const char *bf, const char *af, u_int bits, struct bitinfo *binfo) 295 { 296 297 for (; binfo->bitmask != NULL; binfo++) 298 if (bits & binfo->bitmask) 299 printf("%s%s%s", bf, binfo->string, af); 300 } 301 302 /* 303 * Print out SMART attribute thresholds and values 304 */ 305 306 void 307 print_smart_status(void *vbuf, void *tbuf) 308 { 309 struct ata_smart_attributes *value_buf = vbuf; 310 struct ata_smart_thresholds *threshold_buf = tbuf; 311 int values[256]; 312 int thresholds[256]; 313 int flags[256]; 314 int i, j; 315 int id; 316 int8_t checksum; 317 318 for (i = checksum = 0; i < 511; i++) 319 checksum += ((int8_t *) value_buf)[i]; 320 checksum *= -1; 321 if (checksum != value_buf->checksum) { 322 fprintf(stderr, "SMART attribute values checksum error\n"); 323 return; 324 } 325 326 for (i = checksum = 0; i < 511; i++) 327 checksum += ((int8_t *) threshold_buf)[i]; 328 checksum *= -1; 329 if (checksum != threshold_buf->checksum) { 330 fprintf(stderr, "SMART attribute thresholds checksum error\n"); 331 return; 332 } 333 334 memset(values, 0, sizeof(values)); 335 memset(thresholds, 0, sizeof(thresholds)); 336 memset(flags, 0, sizeof(flags)); 337 338 for (i = 0; i < 30; i++) { 339 id = value_buf->attributes[i].id; 340 values[id] = value_buf->attributes[i].value; 341 flags[id] = value_buf->attributes[i].flags; 342 id = threshold_buf->thresholds[i].id; 343 thresholds[id] = threshold_buf->thresholds[i].value; 344 } 345 346 printf("id\tvalue\tthresh\tcrit\tcollect\treliability description\n"); 347 for (i = 0; i < 256; i++) { 348 if (values[i] != 00 && values[i] != 0xFE && values[i] != 0xFF) { 349 for (j = 0; smart_attrs[j].id != i && smart_attrs[j].id != 0; j++); 350 printf("%3d\t%3d\t%3d\t%s\t%sline\t%stive %s\n", 351 i, values[i], thresholds[i], 352 flags[i] & WDSM_ATTR_ADVISORY ? "yes" : "no", 353 flags[i] & WDSM_ATTR_COLLECTIVE ? "on" : "off", 354 values[i] > thresholds[i] ? "posi" : "nega", 355 smart_attrs[j].name); 356 } 357 } 358 } 359 360 /* 361 * is_smart: 362 * 363 * Detect whether device supports SMART and SMART is enabled. 364 */ 365 366 int 367 is_smart(void) 368 { 369 int retval = 0; 370 struct atareq req; 371 unsigned char inbuf[DEV_BSIZE]; 372 struct ataparams *inqbuf; 373 char *status; 374 375 memset(&inbuf, 0, sizeof(inbuf)); 376 memset(&req, 0, sizeof(req)); 377 378 inqbuf = (struct ataparams *) inbuf; 379 380 req.flags = ATACMD_READ; 381 req.command = WDCC_IDENTIFY; 382 req.databuf = (caddr_t) inbuf; 383 req.datalen = sizeof(inbuf); 384 req.timeout = 1000; 385 386 ata_command(&req); 387 388 if (inqbuf->atap_cmd_def != 0 && inqbuf->atap_cmd_def != 0xffff) { 389 if (!(inqbuf->atap_cmd_set1 & WDC_CMD1_SMART)) { 390 fprintf(stderr, "SMART unsupported\n"); 391 } else { 392 if (inqbuf->atap_ata_major <= WDC_VER_ATA5 || 393 inqbuf->atap_cmd_set2 == 0xffff || 394 inqbuf->atap_cmd_set2 == 0x0000) { 395 status = "status unknown"; 396 retval = 2; 397 } else { 398 if (inqbuf->atap_cmd1_en & WDC_CMD1_SMART) { 399 status = "enabled"; 400 retval = 1; 401 } else { 402 status = "disabled"; 403 } 404 } 405 printf("SMART supported, SMART %s\n", status); 406 } 407 } 408 return retval; 409 } 410 411 /* 412 * DEVICE COMMANDS 413 */ 414 415 /* 416 * device_identify: 417 * 418 * Display the identity of the device 419 */ 420 void 421 device_identify(int argc, char *argv[]) 422 { 423 struct ataparams *inqbuf; 424 struct atareq req; 425 unsigned char inbuf[DEV_BSIZE]; 426 #if BYTE_ORDER == LITTLE_ENDIAN 427 int i; 428 u_int16_t *p; 429 #endif 430 431 /* No arguments. */ 432 if (argc != 0) 433 usage(); 434 435 memset(&inbuf, 0, sizeof(inbuf)); 436 memset(&req, 0, sizeof(req)); 437 438 inqbuf = (struct ataparams *) inbuf; 439 440 req.flags = ATACMD_READ; 441 req.command = WDCC_IDENTIFY; 442 req.databuf = (caddr_t) inbuf; 443 req.datalen = sizeof(inbuf); 444 req.timeout = 1000; 445 446 ata_command(&req); 447 448 #if BYTE_ORDER == LITTLE_ENDIAN 449 /* 450 * On little endian machines, we need to shuffle the string 451 * byte order. However, we don't have to do this for NEC or 452 * Mitsumi ATAPI devices 453 */ 454 455 if (!((inqbuf->atap_config & WDC_CFG_ATAPI_MASK) == WDC_CFG_ATAPI && 456 ((inqbuf->atap_model[0] == 'N' && 457 inqbuf->atap_model[1] == 'E') || 458 (inqbuf->atap_model[0] == 'F' && 459 inqbuf->atap_model[1] == 'X')))) { 460 for (i = 0 ; i < sizeof(inqbuf->atap_model); i += 2) { 461 p = (u_short *) (inqbuf->atap_model + i); 462 *p = ntohs(*p); 463 } 464 for (i = 0 ; i < sizeof(inqbuf->atap_serial); i += 2) { 465 p = (u_short *) (inqbuf->atap_serial + i); 466 *p = ntohs(*p); 467 } 468 for (i = 0 ; i < sizeof(inqbuf->atap_revision); i += 2) { 469 p = (u_short *) (inqbuf->atap_revision + i); 470 *p = ntohs(*p); 471 } 472 } 473 #endif 474 475 /* 476 * Strip blanks off of the info strings. Yuck, I wish this was 477 * cleaner. 478 */ 479 480 if (inqbuf->atap_model[sizeof(inqbuf->atap_model) - 1] == ' ') { 481 inqbuf->atap_model[sizeof(inqbuf->atap_model) - 1] = '\0'; 482 while (inqbuf->atap_model[strlen(inqbuf->atap_model) - 1] == ' ') 483 inqbuf->atap_model[strlen(inqbuf->atap_model) - 1] = '\0'; 484 } 485 486 if (inqbuf->atap_revision[sizeof(inqbuf->atap_revision) - 1] == ' ') { 487 inqbuf->atap_revision[sizeof(inqbuf->atap_revision) - 1] = '\0'; 488 while (inqbuf->atap_revision[strlen(inqbuf->atap_revision) - 1] == ' ') 489 inqbuf->atap_revision[strlen(inqbuf->atap_revision) - 1] = '\0'; 490 } 491 492 if (inqbuf->atap_serial[sizeof(inqbuf->atap_serial) - 1] == ' ') { 493 inqbuf->atap_serial[sizeof(inqbuf->atap_serial) - 1] = '\0'; 494 while (inqbuf->atap_serial[strlen(inqbuf->atap_serial) - 1] == ' ') 495 inqbuf->atap_serial[strlen(inqbuf->atap_serial) - 1] = '\0'; 496 } 497 498 printf("Model: %.*s, Rev: %.*s, Serial #: %.*s\n", 499 (int) sizeof(inqbuf->atap_model), inqbuf->atap_model, 500 (int) sizeof(inqbuf->atap_revision), inqbuf->atap_revision, 501 (int) sizeof(inqbuf->atap_serial), inqbuf->atap_serial); 502 503 printf("Device type: %s, %s\n", inqbuf->atap_config & WDC_CFG_ATAPI ? 504 "ATAPI" : "ATA", inqbuf->atap_config & ATA_CFG_FIXED ? "fixed" : 505 "removable"); 506 507 if ((inqbuf->atap_config & WDC_CFG_ATAPI_MASK) == 0) 508 printf("Cylinders: %d, heads: %d, sec/track: %d, total " 509 "sectors: %d\n", inqbuf->atap_cylinders, 510 inqbuf->atap_heads, inqbuf->atap_sectors, 511 (inqbuf->atap_capacity[1] << 16) | 512 inqbuf->atap_capacity[0]); 513 514 if (inqbuf->atap_queuedepth & WDC_QUEUE_DEPTH_MASK) 515 printf("Device supports command queue depth of %d\n", 516 inqbuf->atap_queuedepth & 0xf); 517 518 printf("Device capabilities:\n"); 519 print_bitinfo("\t", "\n", inqbuf->atap_capabilities1, ata_caps); 520 521 if (inqbuf->atap_ata_major != 0 && inqbuf->atap_ata_major != 0xffff) { 522 printf("Device supports following standards:\n"); 523 print_bitinfo("", " ", inqbuf->atap_ata_major, ata_vers); 524 printf("\n"); 525 } 526 527 if (inqbuf->atap_cmd_set1 != 0 && inqbuf->atap_cmd_set1 != 0xffff && 528 inqbuf->atap_cmd_set2 != 0 && inqbuf->atap_cmd_set2 != 0xffff) { 529 printf("Command set support:\n"); 530 print_bitinfo("\t", "\n", inqbuf->atap_cmd_set1, ata_cmd_set1); 531 print_bitinfo("\t", "\n", inqbuf->atap_cmd_set2, ata_cmd_set2); 532 } 533 534 if (inqbuf->atap_cmd_def != 0 && inqbuf->atap_cmd_def != 0xffff) { 535 printf("Command sets/features enabled:\n"); 536 print_bitinfo("\t", "\n", inqbuf->atap_cmd1_en & 537 (WDC_CMD1_SRV | WDC_CMD1_RLSE | WDC_CMD1_AHEAD | 538 WDC_CMD1_CACHE | WDC_CMD1_SEC | WDC_CMD1_SMART), 539 ata_cmd_set1); 540 print_bitinfo("\t", "\n", inqbuf->atap_cmd2_en & 541 (WDC_CMD2_RMSN | ATA_CMD2_APM), ata_cmd_set2); 542 } 543 544 return; 545 } 546 547 /* 548 * device idle: 549 * 550 * issue the IDLE IMMEDIATE command to the drive 551 */ 552 553 void 554 device_idle(int argc, char *argv[]) 555 { 556 struct atareq req; 557 558 /* No arguments. */ 559 if (argc != 0) 560 usage(); 561 562 memset(&req, 0, sizeof(req)); 563 564 if (strcmp(cmdname, "idle") == 0) 565 req.command = WDCC_IDLE_IMMED; 566 else if (strcmp(cmdname, "standby") == 0) 567 req.command = WDCC_STANDBY_IMMED; 568 else 569 req.command = WDCC_SLEEP; 570 571 req.timeout = 1000; 572 573 ata_command(&req); 574 575 return; 576 } 577 578 /* 579 * Set the idle timer on the disk. Set it for either idle mode or 580 * standby mode, depending on how we were invoked. 581 */ 582 583 void 584 device_setidle(int argc, char *argv[]) 585 { 586 unsigned long idle; 587 struct atareq req; 588 char *end; 589 590 /* Only one argument */ 591 if (argc != 1) 592 usage(); 593 594 idle = strtoul(argv[0], &end, 0); 595 596 if (*end != '\0') { 597 fprintf(stderr, "Invalid idle time: \"%s\"\n", argv[0]); 598 exit(1); 599 } 600 601 if (idle > 19800) { 602 fprintf(stderr, "Idle time has a maximum value of 5.5 " 603 "hours\n"); 604 exit(1); 605 } 606 607 if (idle != 0 && idle < 5) { 608 fprintf(stderr, "Idle timer must be at least 5 seconds\n"); 609 exit(1); 610 } 611 612 memset(&req, 0, sizeof(req)); 613 614 if (idle <= 240*5) 615 req.sec_count = idle / 5; 616 else 617 req.sec_count = idle / (30*60) + 240; 618 619 req.command = cmdname[3] == 's' ? WDCC_STANDBY : WDCC_IDLE; 620 req.timeout = 1000; 621 622 ata_command(&req); 623 624 return; 625 } 626 627 /* 628 * Query the device for the current power mode 629 */ 630 631 void 632 device_checkpower(int argc, char *argv[]) 633 { 634 struct atareq req; 635 636 /* No arguments. */ 637 if (argc != 0) 638 usage(); 639 640 memset(&req, 0, sizeof(req)); 641 642 req.command = WDCC_CHECK_PWR; 643 req.timeout = 1000; 644 req.flags = ATACMD_READREG; 645 646 ata_command(&req); 647 648 printf("Current power status: "); 649 650 switch (req.sec_count) { 651 case 0x00: 652 printf("Standby mode\n"); 653 break; 654 case 0x80: 655 printf("Idle mode\n"); 656 break; 657 case 0xff: 658 printf("Active mode\n"); 659 break; 660 default: 661 printf("Unknown power code (%02x)\n", req.sec_count); 662 } 663 664 return; 665 } 666 667 /* 668 * device_smart: 669 * 670 * Display SMART status 671 */ 672 void 673 device_smart(int argc, char *argv[]) 674 { 675 struct atareq req; 676 unsigned char inbuf[DEV_BSIZE]; 677 unsigned char inbuf2[DEV_BSIZE]; 678 679 /* Only one argument */ 680 if (argc != 1) 681 usage(); 682 683 if (strcmp(argv[0], "enable") == 0) { 684 memset(&req, 0, sizeof(req)); 685 686 req.features = WDSM_ENABLE_OPS; 687 req.command = WDCC_SMART; 688 req.cylinder = htole16(WDSMART_CYL); 689 req.timeout = 1000; 690 691 ata_command(&req); 692 693 is_smart(); 694 } else if (strcmp(argv[0], "disable") == 0) { 695 memset(&req, 0, sizeof(req)); 696 697 req.features = WDSM_DISABLE_OPS; 698 req.command = WDCC_SMART; 699 req.cylinder = htole16(WDSMART_CYL); 700 req.timeout = 1000; 701 702 ata_command(&req); 703 704 is_smart(); 705 } else if (strcmp(argv[0], "status") == 0) { 706 if (is_smart()) { 707 memset(&inbuf, 0, sizeof(inbuf)); 708 memset(&req, 0, sizeof(req)); 709 710 req.features = WDSM_STATUS; 711 req.command = WDCC_SMART; 712 req.cylinder = htole16(WDSMART_CYL); 713 req.timeout = 1000; 714 715 ata_command(&req); 716 717 if (req.cylinder != htole16(WDSMART_CYL)) { 718 fprintf(stderr, "Threshold exceeds condition\n"); 719 } 720 721 /* WDSM_RD_DATA and WDSM_RD_THRESHOLDS are optional 722 * features, the following ata_command()'s may error 723 * and exit(). 724 */ 725 726 memset(&inbuf, 0, sizeof(inbuf)); 727 memset(&req, 0, sizeof(req)); 728 729 req.flags = ATACMD_READ; 730 req.features = WDSM_RD_DATA; 731 req.command = WDCC_SMART; 732 req.databuf = (caddr_t) inbuf; 733 req.datalen = sizeof(inbuf); 734 req.cylinder = htole16(WDSMART_CYL); 735 req.timeout = 1000; 736 737 ata_command(&req); 738 739 memset(&inbuf2, 0, sizeof(inbuf2)); 740 memset(&req, 0, sizeof(req)); 741 742 req.flags = ATACMD_READ; 743 req.features = WDSM_RD_THRESHOLDS; 744 req.command = WDCC_SMART; 745 req.databuf = (caddr_t) inbuf2; 746 req.datalen = sizeof(inbuf2); 747 req.cylinder = htole16(WDSMART_CYL); 748 req.timeout = 1000; 749 750 ata_command(&req); 751 752 print_smart_status(inbuf, inbuf2); 753 } else { 754 fprintf(stderr, "SMART not supported\n"); 755 } 756 } else { 757 usage(); 758 } 759 return; 760 } 761