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