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