[BACK]Return to atactl.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sbin / atactl

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>