1 /* $NetBSD: atactl.c,v 1.14 2001/09/07 16:33:50 simonb 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/ic/wdcreg.h> 56 #include <sys/ataio.h> 57 58 struct command { 59 const char *cmd_name; 60 const char *arg_names; 61 void (*cmd_func)(int, char *[]); 62 }; 63 64 struct bitinfo { 65 u_int bitmask; 66 const char *string; 67 }; 68 69 int main(int, char *[]); 70 void usage(void); 71 void ata_command(struct atareq *); 72 void print_bitinfo(const char *, 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 const char *argnames; /* helpstring: expected arguments */ 79 80 void device_identify(int, char *[]); 81 void device_setidle(int, char *[]); 82 void device_idle(int, char *[]); 83 void device_checkpower(int, char *[]); 84 85 struct command commands[] = { 86 { "identify", "", device_identify }, 87 { "setidle", "idle-timer", device_setidle }, 88 { "setstandby", "standby-timer", device_setidle }, 89 { "idle", "", device_idle }, 90 { "standby", "", device_idle }, 91 { "sleep", "", device_idle }, 92 { "checkpower", "", device_checkpower }, 93 { NULL, NULL, NULL }, 94 }; 95 96 /* 97 * Tables containing bitmasks used for error reporting and 98 * device identification. 99 */ 100 101 struct bitinfo ata_caps[] = { 102 { ATA_CAP_STBY, "ATA standby timer values" }, 103 { WDC_CAP_IORDY, "IORDY operation" }, 104 { WDC_CAP_IORDY_DSBL, "IORDY disabling" }, 105 { NULL, NULL }, 106 }; 107 108 struct bitinfo ata_vers[] = { 109 { WDC_VER_ATA1, "ATA-1" }, 110 { WDC_VER_ATA2, "ATA-2" }, 111 { WDC_VER_ATA3, "ATA-3" }, 112 { WDC_VER_ATA4, "ATA-4" }, 113 { NULL, NULL }, 114 }; 115 116 struct bitinfo ata_cmd_set1[] = { 117 { WDC_CMD1_NOP, "NOP command" }, 118 { WDC_CMD1_RB, "READ BUFFER command" }, 119 { WDC_CMD1_WB, "WRITE BUFFER command" }, 120 { WDC_CMD1_HPA, "Host Protected Area feature set" }, 121 { WDC_CMD1_DVRST, "DEVICE RESET command" }, 122 { WDC_CMD1_SRV, "SERVICE interrupt" }, 123 { WDC_CMD1_RLSE, "release interrupt" }, 124 { WDC_CMD1_AHEAD, "look-ahead" }, 125 { WDC_CMD1_CACHE, "write cache" }, 126 { WDC_CMD1_PKT, "PACKET command feature set" }, 127 { WDC_CMD1_PM, "Power Management feature set" }, 128 { WDC_CMD1_REMOV, "Removable Media feature set" }, 129 { WDC_CMD1_SEC, "Security Mode feature set" }, 130 { WDC_CMD1_SMART, "SMART feature set" }, 131 { NULL, NULL }, 132 }; 133 134 struct bitinfo ata_cmd_set2[] = { 135 { WDC_CMD2_RMSN, "Removable Media Status Notification feature set" }, 136 { ATA_CMD2_APM, "Advanced Power Management feature set" }, 137 { ATA_CMD2_CFA, "CFA feature set" }, 138 { ATA_CMD2_RWQ, "READ/WRITE DMA QUEUED commands" }, 139 { WDC_CMD2_DM, "DOWNLOAD MICROCODE command" }, 140 { NULL, NULL }, 141 }; 142 143 int 144 main(int argc, char *argv[]) 145 { 146 int i; 147 148 /* Must have at least: device command */ 149 if (argc < 3) 150 usage(); 151 152 /* Skip program name, get and skip device name and command. */ 153 dvname = argv[1]; 154 cmdname = argv[2]; 155 argv += 3; 156 argc -= 3; 157 158 /* 159 * Open the device 160 */ 161 fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0); 162 if (fd == -1) { 163 if (errno == ENOENT) { 164 /* 165 * Device doesn't exist. Probably trying to open 166 * a device which doesn't use disk semantics for 167 * device name. Try again, specifying "cooked", 168 * which leaves off the "r" in front of the device's 169 * name. 170 */ 171 fd = opendisk(dvname, O_RDWR, dvname_store, 172 sizeof(dvname_store), 1); 173 if (fd == -1) 174 err(1, "%s", dvname); 175 } else 176 err(1, "%s", dvname); 177 } 178 179 /* 180 * Point the dvname at the actual device name that opendisk() opened. 181 */ 182 dvname = dvname_store; 183 184 /* Look up and call the command. */ 185 for (i = 0; commands[i].cmd_name != NULL; i++) 186 if (strcmp(cmdname, commands[i].cmd_name) == 0) 187 break; 188 if (commands[i].cmd_name == NULL) 189 errx(1, "unknown command: %s", cmdname); 190 191 argnames = commands[i].arg_names; 192 193 (*commands[i].cmd_func)(argc, argv); 194 exit(0); 195 } 196 197 void 198 usage(void) 199 { 200 int i; 201 202 fprintf(stderr, "Usage: %s device command [arg [...]]\n", 203 getprogname()); 204 205 fprintf(stderr, " Available device commands:\n"); 206 for (i=0; commands[i].cmd_name != NULL; i++) 207 fprintf(stderr, "\t%s %s\n", commands[i].cmd_name, 208 commands[i].arg_names); 209 210 exit(1); 211 } 212 213 /* 214 * Wrapper that calls ATAIOCCOMMAND and checks for errors 215 */ 216 217 void 218 ata_command(struct atareq *req) 219 { 220 int error; 221 222 error = ioctl(fd, ATAIOCCOMMAND, req); 223 224 if (error == -1) 225 err(1, "ATAIOCCOMMAND failed"); 226 227 switch (req->retsts) { 228 229 case ATACMD_OK: 230 return; 231 case ATACMD_TIMEOUT: 232 fprintf(stderr, "ATA command timed out\n"); 233 exit(1); 234 case ATACMD_DF: 235 fprintf(stderr, "ATA device returned a Device Fault\n"); 236 exit(1); 237 case ATACMD_ERROR: 238 if (req->error & WDCE_ABRT) 239 fprintf(stderr, "ATA device returned Aborted " 240 "Command\n"); 241 else 242 fprintf(stderr, "ATA device returned error register " 243 "%0x\n", req->error); 244 exit(1); 245 default: 246 fprintf(stderr, "ATAIOCCOMMAND returned unknown result code " 247 "%d\n", req->retsts); 248 exit(1); 249 } 250 } 251 252 /* 253 * Print out strings associated with particular bitmasks 254 */ 255 256 void 257 print_bitinfo(const char *bf, const char *af, u_int bits, struct bitinfo *binfo) 258 { 259 260 for (; binfo->bitmask != NULL; binfo++) 261 if (bits & binfo->bitmask) 262 printf("%s%s%s", bf, binfo->string, af); 263 } 264 265 /* 266 * DEVICE COMMANDS 267 */ 268 269 /* 270 * device_identify: 271 * 272 * Display the identity of the device 273 */ 274 void 275 device_identify(int argc, char *argv[]) 276 { 277 struct ataparams *inqbuf; 278 struct atareq req; 279 unsigned char inbuf[DEV_BSIZE]; 280 #if BYTE_ORDER == LITTLE_ENDIAN 281 int i; 282 u_int16_t *p; 283 #endif 284 285 /* No arguments. */ 286 if (argc != 0) 287 usage(); 288 289 memset(&inbuf, 0, sizeof(inbuf)); 290 memset(&req, 0, sizeof(req)); 291 292 inqbuf = (struct ataparams *) inbuf; 293 294 req.flags = ATACMD_READ; 295 req.command = WDCC_IDENTIFY; 296 req.databuf = (caddr_t) inbuf; 297 req.datalen = sizeof(inbuf); 298 req.timeout = 1000; 299 300 ata_command(&req); 301 302 #if BYTE_ORDER == LITTLE_ENDIAN 303 /* 304 * On little endian machines, we need to shuffle the string 305 * byte order. However, we don't have to do this for NEC or 306 * Mitsumi ATAPI devices 307 */ 308 309 if (!((inqbuf->atap_config & WDC_CFG_ATAPI_MASK) == WDC_CFG_ATAPI && 310 ((inqbuf->atap_model[0] == 'N' && 311 inqbuf->atap_model[1] == 'E') || 312 (inqbuf->atap_model[0] == 'F' && 313 inqbuf->atap_model[1] == 'X')))) { 314 for (i = 0 ; i < sizeof(inqbuf->atap_model); i += 2) { 315 p = (u_short *) (inqbuf->atap_model + i); 316 *p = ntohs(*p); 317 } 318 for (i = 0 ; i < sizeof(inqbuf->atap_serial); i += 2) { 319 p = (u_short *) (inqbuf->atap_serial + i); 320 *p = ntohs(*p); 321 } 322 for (i = 0 ; i < sizeof(inqbuf->atap_revision); i += 2) { 323 p = (u_short *) (inqbuf->atap_revision + i); 324 *p = ntohs(*p); 325 } 326 } 327 #endif 328 329 /* 330 * Strip blanks off of the info strings. Yuck, I wish this was 331 * cleaner. 332 */ 333 334 if (inqbuf->atap_model[sizeof(inqbuf->atap_model) - 1] == ' ') { 335 inqbuf->atap_model[sizeof(inqbuf->atap_model) - 1] = '\0'; 336 while (inqbuf->atap_model[strlen(inqbuf->atap_model) - 1] == ' ') 337 inqbuf->atap_model[strlen(inqbuf->atap_model) - 1] = '\0'; 338 } 339 340 if (inqbuf->atap_revision[sizeof(inqbuf->atap_revision) - 1] == ' ') { 341 inqbuf->atap_revision[sizeof(inqbuf->atap_revision) - 1] = '\0'; 342 while (inqbuf->atap_revision[strlen(inqbuf->atap_revision) - 1] == ' ') 343 inqbuf->atap_revision[strlen(inqbuf->atap_revision) - 1] = '\0'; 344 } 345 346 if (inqbuf->atap_serial[sizeof(inqbuf->atap_serial) - 1] == ' ') { 347 inqbuf->atap_serial[sizeof(inqbuf->atap_serial) - 1] = '\0'; 348 while (inqbuf->atap_serial[strlen(inqbuf->atap_serial) - 1] == ' ') 349 inqbuf->atap_serial[strlen(inqbuf->atap_serial) - 1] = '\0'; 350 } 351 352 printf("Model: %.*s, Rev: %.*s, Serial #: %.*s\n", 353 (int) sizeof(inqbuf->atap_model), inqbuf->atap_model, 354 (int) sizeof(inqbuf->atap_revision), inqbuf->atap_revision, 355 (int) sizeof(inqbuf->atap_serial), inqbuf->atap_serial); 356 357 printf("Device type: %s, %s\n", inqbuf->atap_config & WDC_CFG_ATAPI ? 358 "ATAPI" : "ATA", inqbuf->atap_config & ATA_CFG_FIXED ? "fixed" : 359 "removable"); 360 361 if ((inqbuf->atap_config & WDC_CFG_ATAPI_MASK) == 0) 362 printf("Cylinders: %d, heads: %d, sec/track: %d, total " 363 "sectors: %d\n", inqbuf->atap_cylinders, 364 inqbuf->atap_heads, inqbuf->atap_sectors, 365 (inqbuf->atap_capacity[1] << 16) | 366 inqbuf->atap_capacity[0]); 367 368 if (inqbuf->atap_queuedepth & WDC_QUEUE_DEPTH_MASK) 369 printf("Device supports command queue depth of %d\n", 370 inqbuf->atap_queuedepth & 0xf); 371 372 printf("Device capabilities:\n"); 373 print_bitinfo("\t", "\n", inqbuf->atap_capabilities1, ata_caps); 374 375 if (inqbuf->atap_ata_major != 0 && inqbuf->atap_ata_major != 0xffff) { 376 printf("Device supports following standards:\n"); 377 print_bitinfo("", " ", inqbuf->atap_ata_major, ata_vers); 378 printf("\n"); 379 } 380 381 if (inqbuf->atap_cmd_set1 != 0 && inqbuf->atap_cmd_set1 != 0xffff && 382 inqbuf->atap_cmd_set2 != 0 && inqbuf->atap_cmd_set2 != 0xffff) { 383 printf("Command set support:\n"); 384 print_bitinfo("\t", "\n", inqbuf->atap_cmd_set1, ata_cmd_set1); 385 print_bitinfo("\t", "\n", inqbuf->atap_cmd_set2, ata_cmd_set2); 386 } 387 388 if (inqbuf->atap_cmd_def != 0 && inqbuf->atap_cmd_def != 0xffff) { 389 printf("Command sets/features enabled:\n"); 390 print_bitinfo("\t", "\n", inqbuf->atap_cmd1_en & 391 (WDC_CMD1_SRV | WDC_CMD1_RLSE | WDC_CMD1_AHEAD | 392 WDC_CMD1_CACHE | WDC_CMD1_SEC | WDC_CMD1_SMART), 393 ata_cmd_set1); 394 print_bitinfo("\t", "\n", inqbuf->atap_cmd2_en & 395 (WDC_CMD2_RMSN | ATA_CMD2_APM), ata_cmd_set2); 396 } 397 398 return; 399 } 400 401 /* 402 * device idle: 403 * 404 * issue the IDLE IMMEDIATE command to the drive 405 */ 406 407 void 408 device_idle(int argc, char *argv[]) 409 { 410 struct atareq req; 411 412 /* No arguments. */ 413 if (argc != 0) 414 usage(); 415 416 memset(&req, 0, sizeof(req)); 417 418 if (strcmp(cmdname, "idle") == 0) 419 req.command = WDCC_IDLE_IMMED; 420 else if (strcmp(cmdname, "standby") == 0) 421 req.command = WDCC_STANDBY_IMMED; 422 else 423 req.command = WDCC_SLEEP; 424 425 req.timeout = 1000; 426 427 ata_command(&req); 428 429 return; 430 } 431 432 /* 433 * Set the idle timer on the disk. Set it for either idle mode or 434 * standby mode, depending on how we were invoked. 435 */ 436 437 void 438 device_setidle(int argc, char *argv[]) 439 { 440 unsigned long idle; 441 struct atareq req; 442 char *end; 443 444 /* Only one argument */ 445 if (argc != 1) 446 usage(); 447 448 idle = strtoul(argv[0], &end, 0); 449 450 if (*end != '\0') { 451 fprintf(stderr, "Invalid idle time: \"%s\"\n", argv[0]); 452 exit(1); 453 } 454 455 if (idle > 19800) { 456 fprintf(stderr, "Idle time has a maximum value of 5.5 " 457 "hours\n"); 458 exit(1); 459 } 460 461 if (idle != 0 && idle < 5) { 462 fprintf(stderr, "Idle timer must be at least 5 seconds\n"); 463 exit(1); 464 } 465 466 memset(&req, 0, sizeof(req)); 467 468 if (idle <= 240*5) 469 req.sec_count = idle / 5; 470 else 471 req.sec_count = idle / (30*60) + 240; 472 473 req.command = cmdname[3] == 's' ? WDCC_STANDBY : WDCC_IDLE; 474 req.timeout = 1000; 475 476 ata_command(&req); 477 478 return; 479 } 480 481 /* 482 * Query the device for the current power mode 483 */ 484 485 void 486 device_checkpower(int argc, char *argv[]) 487 { 488 struct atareq req; 489 490 /* No arguments. */ 491 if (argc != 0) 492 usage(); 493 494 memset(&req, 0, sizeof(req)); 495 496 req.command = WDCC_CHECK_PWR; 497 req.timeout = 1000; 498 req.flags = ATACMD_READREG; 499 500 ata_command(&req); 501 502 printf("Current power status: "); 503 504 switch (req.sec_count) { 505 case 0x00: 506 printf("Standby mode\n"); 507 break; 508 case 0x80: 509 printf("Idle mode\n"); 510 break; 511 case 0xff: 512 printf("Active mode\n"); 513 break; 514 default: 515 printf("Unknown power code (%02x)\n", req.sec_count); 516 } 517 518 return; 519 } 520