1 /* $OpenBSD: atactl.c,v 1.8 2001/07/07 18:26:09 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 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 /* 41 * atactl(8) - a program to control ATA devices. 42 */ 43 44 #include <sys/param.h> 45 #include <sys/ioctl.h> 46 #include <err.h> 47 #include <errno.h> 48 #include <fcntl.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 #include <util.h> 54 55 #include <dev/ata/atareg.h> 56 #include <dev/ic/wdcreg.h> 57 #include <sys/ataio.h> 58 59 struct command { 60 const char *cmd_name; 61 void (*cmd_func) __P((int, char *[])); 62 }; 63 64 struct bitinfo { 65 u_int bitmask; 66 const char *string; 67 }; 68 69 int main __P((int, char *[])); 70 void usage __P((void)); 71 void ata_command __P((struct atareq *)); 72 void print_bitinfo __P((const char *, u_int, struct bitinfo *)); 73 74 int fd; /* file descriptor for device */ 75 const char *dvname; /* device name */ 76 char dvname_store[MAXPATHLEN]; /* for opendisk(3) */ 77 const char *cmdname; /* command user issued */ 78 79 extern const char *__progname; /* from crt0.o */ 80 81 void device_identify __P((int, char *[])); 82 void device_setidle __P((int, char *[])); 83 void device_idle __P((int, char *[])); 84 void device_checkpower __P((int, char *[])); 85 void device_acoustic __P((int, char *[])); 86 void device_apm __P((int, char *[])); 87 void device_feature __P((int, char *[])); 88 void device_smart __P((int, char *[])); 89 90 struct command commands[] = { 91 { "identify", device_identify }, 92 { "setidle", device_setidle }, 93 { "setstandby", device_setidle }, 94 { "idle", device_idle }, 95 { "standby", device_idle }, 96 { "sleep", device_idle }, 97 { "checkpower", device_checkpower }, 98 { "acousticdisable", device_feature }, 99 { "acousticset", device_acoustic }, 100 { "apmdisable", device_feature }, 101 { "apmset", device_apm }, 102 { "poddisable", device_feature }, 103 { "podenable", device_feature }, 104 { "puisdisable", device_feature }, 105 { "puisenable", device_feature }, 106 { "puisspinup", device_feature }, 107 { "readaheaddisable", device_feature }, 108 { "readaheadenable", device_feature }, 109 { "smartenable", device_smart }, 110 { "smartdisable", device_smart }, 111 { "smartstatus", device_smart }, 112 { "writecachedisable", device_feature }, 113 { "writecacheenable", device_feature }, 114 { NULL, NULL }, 115 }; 116 117 /* 118 * Tables containing bitmasks used for error reporting and 119 * device identification. 120 */ 121 122 struct bitinfo ata_caps[] = { 123 { ATA_CAP_STBY, "ATA standby timer values" }, 124 { WDC_CAP_IORDY, "IORDY operation" }, 125 { WDC_CAP_IORDY_DSBL, "IORDY disabling" }, 126 { NULL, NULL }, 127 }; 128 129 struct bitinfo ata_vers[] = { 130 { WDC_VER_ATA1, "ATA-1" }, 131 { WDC_VER_ATA2, "ATA-2" }, 132 { WDC_VER_ATA3, "ATA-3" }, 133 { WDC_VER_ATA4, "ATA-4" }, 134 { WDC_VER_ATA5, "ATA-5" }, 135 { WDC_VER_ATA6, "ATA-6" }, 136 { WDC_VER_ATA7, "ATA-7" }, 137 { WDC_VER_ATA8, "ATA-8" }, 138 { WDC_VER_ATA9, "ATA-9" }, 139 { WDC_VER_ATA10, "ATA-10" }, 140 { WDC_VER_ATA11, "ATA-11" }, 141 { WDC_VER_ATA12, "ATA-12" }, 142 { WDC_VER_ATA13, "ATA-13" }, 143 { WDC_VER_ATA14, "ATA-14" }, 144 { NULL, NULL }, 145 }; 146 147 struct bitinfo ata_cmd_set1[] = { 148 { WDC_CMD1_NOP, "NOP command" }, 149 { WDC_CMD1_RB, "READ BUFFER command" }, 150 { WDC_CMD1_WB, "WRITE BUFFER command" }, 151 { WDC_CMD1_HPA, "Host Protected Area feature set" }, 152 { WDC_CMD1_DVRST, "DEVICE RESET command" }, 153 { WDC_CMD1_SRV, "SERVICE interrupt" }, 154 { WDC_CMD1_RLSE, "release interrupt" }, 155 { WDC_CMD1_AHEAD, "read look-ahead" }, 156 { WDC_CMD1_CACHE, "write cache" }, 157 { WDC_CMD1_PKT, "PACKET command feature set" }, 158 { WDC_CMD1_PM, "Power Management feature set" }, 159 { WDC_CMD1_REMOV, "Removable Media feature set" }, 160 { WDC_CMD1_SEC, "Security Mode feature set" }, 161 { WDC_CMD1_SMART, "SMART feature set" }, 162 { NULL, NULL }, 163 }; 164 165 struct bitinfo ata_cmd_set2[] = { 166 { ATAPI_CMD2_FCE, "Flush Cache Ext command" }, 167 { ATAPI_CMD2_FC, "Flush Cache command" }, 168 { ATAPI_CMD2_DCO, "Device Configuration Overlay feature set" }, 169 { ATAPI_CMD2_48AD, "48bit address feature set" }, 170 { ATAPI_CMD2_AAM, "Automatic Acoustic Management feature set" }, 171 { ATAPI_CMD2_SM, "Set Max security extension commands" }, 172 { ATAPI_CMD2_SF, "Set Features subcommand required" }, 173 { ATAPI_CMD2_PUIS, "Power-up in standby feature set" }, 174 { WDC_CMD2_RMSN, "Removable Media Status Notification feature set" }, 175 { ATA_CMD2_APM, "Advanced Power Management feature set" }, 176 { ATA_CMD2_CFA, "CFA feature set" }, 177 { ATA_CMD2_RWQ, "READ/WRITE DMA QUEUED commands" }, 178 { WDC_CMD2_DM, "DOWNLOAD MICROCODE command" }, 179 { NULL, NULL }, 180 }; 181 182 struct bitinfo ata_cmd_ext[] = { 183 { ATAPI_CMDE_MSER, "Media serial number" }, 184 { ATAPI_CMDE_TEST, "SMART self-test" }, 185 { ATAPI_CMDE_SLOG, "SMART error logging" }, 186 { NULL, NULL }, 187 }; 188 189 int 190 main(argc, argv) 191 int argc; 192 char *argv[]; 193 { 194 int i; 195 196 dvname = argv[1]; 197 if (argc == 2) { 198 cmdname = "identify"; 199 argv += 2; 200 argc -= 2; 201 } else if (argc < 3) { 202 usage(); 203 } else { 204 /* Skip program name, get and skip device name and command. */ 205 206 cmdname = argv[2]; 207 argv += 3; 208 argc -= 3; 209 } 210 211 /* 212 * Open the device 213 */ 214 fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0); 215 if (fd == -1) { 216 if (errno == ENOENT) { 217 /* 218 * Device doesn't exist. Probably trying to open 219 * a device which doesn't use disk semantics for 220 * device name. Try again, specifying "cooked", 221 * which leaves off the "r" in front of the device's 222 * name. 223 */ 224 fd = opendisk(dvname, O_RDWR, dvname_store, 225 sizeof(dvname_store), 1); 226 if (fd == -1) 227 err(1, "%s", dvname); 228 } else 229 err(1, "%s", dvname); 230 } 231 232 /* 233 * Point the dvname at the actual device name that opendisk() opened. 234 */ 235 dvname = dvname_store; 236 237 /* Look up and call the command. */ 238 for (i = 0; commands[i].cmd_name != NULL; i++) 239 if (strcmp(cmdname, commands[i].cmd_name) == 0) 240 break; 241 if (commands[i].cmd_name == NULL) 242 errx(1, "unknown command: %s\n", cmdname); 243 244 (*commands[i].cmd_func)(argc, argv); 245 246 return (0); 247 } 248 249 void 250 usage() 251 { 252 253 fprintf(stderr, "usage: %s device command [arg [...]]\n", 254 __progname); 255 exit(1); 256 } 257 258 /* 259 * Wrapper that calls ATAIOCCOMMAND and checks for errors 260 */ 261 262 void 263 ata_command(req) 264 struct atareq *req; 265 { 266 int error; 267 268 error = ioctl(fd, ATAIOCCOMMAND, req); 269 270 if (error == -1) 271 err(1, "ATAIOCCOMMAND failed"); 272 273 switch (req->retsts) { 274 275 case ATACMD_OK: 276 return; 277 case ATACMD_TIMEOUT: 278 fprintf(stderr, "ATA command timed out\n"); 279 exit(1); 280 case ATACMD_DF: 281 fprintf(stderr, "ATA device returned a Device Fault\n"); 282 exit(1); 283 case ATACMD_ERROR: 284 if (req->error & WDCE_ABRT) 285 fprintf(stderr, "ATA device returned Aborted " 286 "Command\n"); 287 else 288 fprintf(stderr, "ATA device returned error register " 289 "%0x\n", req->error); 290 exit(1); 291 default: 292 fprintf(stderr, "ATAIOCCOMMAND returned unknown result code " 293 "%d\n", req->retsts); 294 exit(1); 295 } 296 } 297 298 /* 299 * Print out strings associated with particular bitmasks 300 */ 301 302 void 303 print_bitinfo(f, bits, binfo) 304 const char *f; 305 u_int bits; 306 struct bitinfo *binfo; 307 { 308 309 for (; binfo->bitmask != NULL; binfo++) 310 if (bits & binfo->bitmask) 311 printf(f, binfo->string); 312 } 313 314 /* 315 * DEVICE COMMANDS 316 */ 317 318 /* 319 * device_identify: 320 * 321 * Display the identity of the device 322 */ 323 void 324 device_identify(argc, argv) 325 int argc; 326 char *argv[]; 327 { 328 struct ataparams *inqbuf; 329 struct atareq req; 330 unsigned char inbuf[DEV_BSIZE]; 331 #if BYTE_ORDER == LITTLE_ENDIAN 332 int i; 333 u_int16_t *p; 334 #endif 335 336 /* No arguments. */ 337 if (argc != 0) 338 goto usage; 339 340 memset(&inbuf, 0, sizeof(inbuf)); 341 memset(&req, 0, sizeof(req)); 342 343 inqbuf = (struct ataparams *) inbuf; 344 345 req.flags = ATACMD_READ; 346 req.command = WDCC_IDENTIFY; 347 req.databuf = (caddr_t) inbuf; 348 req.datalen = sizeof(inbuf); 349 req.timeout = 1000; 350 351 ata_command(&req); 352 353 #if BYTE_ORDER == LITTLE_ENDIAN 354 /* 355 * On little endian machines, we need to shuffle the string 356 * byte order. However, we don't have to do this for NEC or 357 * Mitsumi ATAPI devices 358 */ 359 360 if (!((inqbuf->atap_config & WDC_CFG_ATAPI_MASK) == WDC_CFG_ATAPI && 361 ((inqbuf->atap_model[0] == 'N' && 362 inqbuf->atap_model[1] == 'E') || 363 (inqbuf->atap_model[0] == 'F' && 364 inqbuf->atap_model[1] == 'X')))) { 365 for (i = 0 ; i < sizeof(inqbuf->atap_model); i += 2) { 366 p = (u_short *) (inqbuf->atap_model + i); 367 *p = ntohs(*p); 368 } 369 for (i = 0 ; i < sizeof(inqbuf->atap_serial); i += 2) { 370 p = (u_short *) (inqbuf->atap_serial + i); 371 *p = ntohs(*p); 372 } 373 for (i = 0 ; i < sizeof(inqbuf->atap_revision); i += 2) { 374 p = (u_short *) (inqbuf->atap_revision + i); 375 *p = ntohs(*p); 376 } 377 } 378 #endif 379 380 /* 381 * Strip blanks off of the info strings. Yuck, I wish this was 382 * cleaner. 383 */ 384 385 if (inqbuf->atap_model[sizeof(inqbuf->atap_model) - 1] == ' ') { 386 inqbuf->atap_model[sizeof(inqbuf->atap_model) - 1] = '\0'; 387 while (inqbuf->atap_model[strlen(inqbuf->atap_model) - 1] == ' ') 388 inqbuf->atap_model[strlen(inqbuf->atap_model) - 1] = '\0'; 389 } 390 391 if (inqbuf->atap_revision[sizeof(inqbuf->atap_revision) - 1] == ' ') { 392 inqbuf->atap_revision[sizeof(inqbuf->atap_revision) - 1] = '\0'; 393 while (inqbuf->atap_revision[strlen(inqbuf->atap_revision) - 1] == ' ') 394 inqbuf->atap_revision[strlen(inqbuf->atap_revision) - 1] = '\0'; 395 } 396 397 if (inqbuf->atap_serial[sizeof(inqbuf->atap_serial) - 1] == ' ') { 398 inqbuf->atap_serial[sizeof(inqbuf->atap_serial) - 1] = '\0'; 399 while (inqbuf->atap_serial[strlen(inqbuf->atap_serial) - 1] == ' ') 400 inqbuf->atap_serial[strlen(inqbuf->atap_serial) - 1] = '\0'; 401 } 402 403 printf("Model: %.*s, Rev: %.*s, Serial #: %.*s\n", 404 (int) sizeof(inqbuf->atap_model), inqbuf->atap_model, 405 (int) sizeof(inqbuf->atap_revision), inqbuf->atap_revision, 406 (int) sizeof(inqbuf->atap_serial), inqbuf->atap_serial); 407 408 printf("Device type: %s, %s\n", inqbuf->atap_config & WDC_CFG_ATAPI ? 409 "ATAPI" : "ATA", inqbuf->atap_config & ATA_CFG_FIXED ? "fixed" : 410 "removable"); 411 412 if ((inqbuf->atap_config & WDC_CFG_ATAPI_MASK) == 0) 413 printf("Cylinders: %d, heads: %d, sec/track: %d, total " 414 "sectors: %d\n", inqbuf->atap_cylinders, 415 inqbuf->atap_heads, inqbuf->atap_sectors, 416 (inqbuf->atap_capacity[1] << 16) | 417 inqbuf->atap_capacity[0]); 418 419 if (inqbuf->atap_queuedepth & WDC_QUEUE_DEPTH_MASK) 420 printf("Device supports command queue depth of %d\n", 421 inqbuf->atap_queuedepth & 0xf); 422 423 printf("Device capabilities:\n"); 424 print_bitinfo("\t%s\n", inqbuf->atap_capabilities1, ata_caps); 425 426 if (inqbuf->atap_ata_major != 0 && inqbuf->atap_ata_major != 0xffff) { 427 printf("Device supports following standards:\n"); 428 print_bitinfo("%s ", inqbuf->atap_ata_major, ata_vers); 429 printf("\n"); 430 } 431 432 if (inqbuf->atap_cmd_set1 != 0 && inqbuf->atap_cmd_set1 != 0xffff && 433 inqbuf->atap_cmd_set2 != 0 && inqbuf->atap_cmd_set2 != 0xffff) { 434 printf("Device supports the following command sets:\n"); 435 print_bitinfo("\t%s\n", inqbuf->atap_cmd_set1, ata_cmd_set1); 436 print_bitinfo("\t%s\n", inqbuf->atap_cmd_set2, ata_cmd_set2); 437 print_bitinfo("\t%s\n", inqbuf->atap_cmd_ext, ata_cmd_ext); 438 } 439 440 if (inqbuf->atap_cmd_def != 0 && inqbuf->atap_cmd_def != 0xffff) { 441 printf("Device has enabled the following command sets/features:\n"); 442 print_bitinfo("\t%s\n", inqbuf->atap_cmd1_en, ata_cmd_set1); 443 print_bitinfo("\t%s\n", inqbuf->atap_cmd2_en, ata_cmd_set2); 444 #if 0 445 print_bitinfo("\t%s\n", inqbuf->atap_cmd_set1 & 446 (WDC_CMD1_SRV | WDC_CMD1_RLSE | WDC_CMD1_AHEAD | 447 WDC_CMD1_CACHE | WDC_CMD1_SEC | WDC_CMD1_SMART), 448 ata_cmd_set1); 449 print_bitinfo("\t%s\n", inqbuf->atap_cmd_set2 & 450 (WDC_CMD2_RMSN | ATA_CMD2_APM | ATAPI_CMD2_PUIS | 451 ATAPI_CMD2_AAM | ATAPI_CMD2_48AD | 452 ATAPI_CMD2_DCO), ata_cmd_set2); 453 #endif 454 } 455 456 return; 457 458 usage: 459 fprintf(stderr, "usage: %s device %s\n", __progname, cmdname); 460 exit(1); 461 } 462 463 /* 464 * device idle: 465 * 466 * issue the IDLE IMMEDIATE command to the drive 467 */ 468 469 void 470 device_idle(argc, argv) 471 int argc; 472 char *argv[]; 473 { 474 struct atareq req; 475 476 /* No arguments. */ 477 if (argc != 0) 478 goto usage; 479 480 memset(&req, 0, sizeof(req)); 481 482 if (strcmp(cmdname, "idle") == 0) 483 req.command = WDCC_IDLE_IMMED; 484 else if (strcmp(cmdname, "standby") == 0) 485 req.command = WDCC_STANDBY_IMMED; 486 else 487 req.command = WDCC_SLEEP; 488 489 req.timeout = 1000; 490 491 ata_command(&req); 492 493 return; 494 usage: 495 fprintf(stderr, "usage: %s device %s\n", __progname, cmdname); 496 exit(1); 497 } 498 499 /* 500 * SMART. 501 * 502 * issue the SMART ENABLE/DISABLE/STATUS commands to the drive 503 */ 504 505 void 506 device_smart(argc, argv) 507 int argc; 508 char *argv[]; 509 { 510 struct atareq req; 511 512 /* No arguments. */ 513 if (argc != 0) 514 goto usage; 515 516 memset(&req, 0, sizeof(req)); 517 518 req.command = ATAPI_SMART; 519 req.cylinder = 0xC24F; /* Cylinders is mapped to LBA Mid/Low */ 520 /* XXX: I assume cylinders is correctly mapped w.r.t. 521 * endianness? */ 522 523 if (strcmp(cmdname, "smartenable") == 0) 524 req.features = ATAPI_SMART_EN; 525 else if (strcmp(cmdname, "smartdisable") == 0) 526 req.features = ATAPI_SMART_DS; 527 else if (strcmp(cmdname, "smartstatus") == 0) 528 req.features = ATAPI_SMART_STATUS; 529 else 530 goto usage; 531 532 req.timeout = 1000; 533 534 ata_command(&req); 535 536 if (strcmp(cmdname, "smartstatus") == 0) { 537 if (req.cylinder == 0xC24F) 538 printf("No SMART threshold exceeded\n"); 539 else if (req.cylinder == 0x2CF4) { 540 fprintf(stderr,"SMART threshold exceeded!\n"); 541 exit(2); 542 } else { 543 fprintf(stderr, "Unknown response %02x!\n", 544 req.cylinder); 545 exit(1); 546 } 547 } 548 549 return; 550 usage: 551 fprintf(stderr, "usage: %s device %s\n", __progname, cmdname); 552 exit(1); 553 } 554 555 /* 556 * Set the automatic acoustic managmement on the disk. 557 */ 558 void 559 device_acoustic(argc, argv) 560 int argc; 561 char *argv[]; 562 { 563 unsigned long acoustic; 564 struct atareq req; 565 char *end; 566 567 /* Only one argument */ 568 if (argc != 1) 569 goto usage; 570 571 acoustic = strtoul(argv[0], &end, 0); 572 573 if (*end != '\0') { 574 fprintf(stderr, "Invalid acoustic management value: \"%s\"" 575 "(valid values range from 0 to 126)\n", argv[0]); 576 exit(1); 577 } 578 579 if (acoustic > 126) { 580 fprintf(stderr, "Automatic acoustic management has a " 581 "maximum value of 126\n"); 582 exit(1); 583 } 584 585 memset(&req, 0, sizeof(req)); 586 587 req.sec_count = acoustic + 0x80; 588 589 req.command = SET_FEATURES ; 590 req.features = WDSF_AAM_EN ; 591 req.timeout = 1000; 592 593 ata_command(&req); 594 595 return; 596 597 usage: 598 fprintf(stderr, "usage; %s device %s acoustic-management-value\n", 599 __progname, cmdname); 600 exit(1); 601 } 602 603 /* 604 * Set the advanced power managmement on the disk. Power management 605 * levels are translated from user-range 0-253 to ATAPI levels 1-0xFD 606 * to keep a uniform interface to the user. 607 */ 608 void 609 device_apm(argc, argv) 610 int argc; 611 char *argv[]; 612 { 613 unsigned long power; 614 struct atareq req; 615 char *end; 616 617 /* Only one argument */ 618 if (argc != 1) 619 goto usage; 620 621 power = strtoul(argv[0], &end, 0); 622 623 if (*end != '\0') { 624 fprintf(stderr, "Invalid advanced power management value: " 625 "\"%s\" (valid values range from 0 to 253)\n", 626 argv[0]); 627 exit(1); 628 } 629 630 if (power > 253) { 631 fprintf(stderr, "Advanced power management has a " 632 "maximum value of 253\n"); 633 exit(1); 634 } 635 636 memset(&req, 0, sizeof(req)); 637 638 req.sec_count = power + 0x01; 639 640 req.command = SET_FEATURES ; 641 req.features = WDSF_APM_EN ; 642 req.timeout = 1000; 643 644 ata_command(&req); 645 646 return; 647 648 usage: 649 fprintf(stderr, "usage; %s device %s power-management-level\n", 650 __progname, cmdname); 651 exit(1); 652 } 653 654 /* 655 * En/disable features (the automatic acoustic managmement, Advanced Power 656 * Management) on the disk. 657 */ 658 void 659 device_feature(argc, argv) 660 int argc; 661 char *argv[]; 662 { 663 struct atareq req; 664 665 /* No argument */ 666 if (argc != 0) 667 goto usage; 668 669 memset(&req, 0, sizeof(req)); 670 671 req.command = SET_FEATURES ; 672 673 if (strcmp(cmdname, "acousticdisable") == 0) 674 req.features = WDSF_AAM_DS; 675 else if (strcmp(cmdname, "readaheadenable") == 0) 676 req.features = WDSF_READAHEAD_EN; 677 else if (strcmp(cmdname, "readaheaddisable") == 0) 678 req.features = WDSF_READAHEAD_DS; 679 else if (strcmp(cmdname, "writecacheenable") == 0) 680 req.features = WDSF_EN_WR_CACHE; 681 else if (strcmp(cmdname, "writecachedisable") == 0) 682 req.features = WDSF_WRITE_CACHE_DS; 683 else if (strcmp(cmdname, "apmdisable") == 0) 684 req.features = WDSF_APM_DS; 685 else if (strcmp(cmdname, "puisenable") == 0) 686 req.features = WDSF_PUIS_EN; 687 else if (strcmp(cmdname, "puisdisable") == 0) 688 req.features = WDSF_PUIS_DS; 689 else if (strcmp(cmdname, "puisspinup") == 0) 690 req.features = WDSF_PUIS_SPINUP; 691 else 692 goto usage; 693 694 req.timeout = 1000; 695 696 ata_command(&req); 697 698 return; 699 700 usage: 701 fprintf(stderr, "usage; %s device %s\n", __progname, 702 cmdname); 703 exit(1); 704 } 705 706 /* 707 * Set the idle timer on the disk. Set it for either idle mode or 708 * standby mode, depending on how we were invoked. 709 */ 710 711 void 712 device_setidle(argc, argv) 713 int argc; 714 char *argv[]; 715 { 716 unsigned long idle; 717 struct atareq req; 718 char *end; 719 720 /* Only one argument */ 721 if (argc != 1) 722 goto usage; 723 724 idle = strtoul(argv[0], &end, 0); 725 726 if (*end != '\0') { 727 fprintf(stderr, "Invalid idle time: \"%s\"\n", argv[0]); 728 exit(1); 729 } 730 731 if (idle > 19800) { 732 fprintf(stderr, "Idle time has a maximum value of 5.5 " 733 "hours\n"); 734 exit(1); 735 } 736 737 if (idle != 0 && idle < 5) { 738 fprintf(stderr, "Idle timer must be at least 5 seconds\n"); 739 exit(1); 740 } 741 742 memset(&req, 0, sizeof(req)); 743 744 if (idle <= 240*5) 745 req.sec_count = idle / 5; 746 else 747 req.sec_count = idle / (30*60) + 240; 748 749 req.command = cmdname[3] == 's' ? WDCC_STANDBY : WDCC_IDLE; 750 req.timeout = 1000; 751 752 ata_command(&req); 753 754 return; 755 756 usage: 757 fprintf(stderr, "usage; %s device %s idle-time\n", __progname, 758 cmdname); 759 exit(1); 760 } 761 762 /* 763 * Query the device for the current power mode 764 */ 765 766 void 767 device_checkpower(argc, argv) 768 int argc; 769 char *argv[]; 770 { 771 struct atareq req; 772 773 /* No arguments. */ 774 if (argc != 0) 775 goto usage; 776 777 memset(&req, 0, sizeof(req)); 778 779 req.command = WDCC_CHECK_PWR; 780 req.timeout = 1000; 781 req.flags = ATACMD_READREG; 782 783 ata_command(&req); 784 785 printf("Current power status: "); 786 787 switch (req.sec_count) { 788 case 0x00: 789 printf("Standby mode\n"); 790 break; 791 case 0x80: 792 printf("Idle mode\n"); 793 break; 794 case 0xff: 795 printf("Active mode\n"); 796 break; 797 default: 798 printf("Unknown power code (%02x)\n", req.sec_count); 799 } 800 801 return; 802 usage: 803 fprintf(stderr, "usage: %s device %s\n", __progname, cmdname); 804 exit(1); 805 } 806