root/usr.sbin/mlxctl/cmds.c
/*      $NetBSD: cmds.c,v 1.13 2011/08/14 17:57:44 christos Exp $       */

/*-
 * Copyright (c) 2001 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Andrew Doran.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*-
 * Copyright (c) 1999 Michael Smith
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * from FreeBSD: command.c,v 1.2 2000/04/11 23:04:17 msmith Exp
 */

#ifndef lint
#include <sys/cdefs.h>
__RCSID("$NetBSD: cmds.c,v 1.13 2011/08/14 17:57:44 christos Exp $");
#endif /* not lint */

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/queue.h>
#include <sys/endian.h>

#include <dev/ic/mlxreg.h>
#include <dev/ic/mlxio.h>

#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "extern.h"

static void     cmd_status0(struct mlx_disk *);
static void     cmd_check0(struct mlx_disk *);
static void     cmd_detach0(struct mlx_disk *);

static struct   mlx_rebuild_status rs;
static int      rstatus;

static struct {
        int     hwid;
        const char      *name;
} const mlx_ctlr_names[] = {
        { 0x00, "960E/960M" },
        { 0x01, "960P/PD" },
        { 0x02, "960PL" },
        { 0x10, "960PG" },
        { 0x11, "960PJ" },
        { 0x12, "960PR" },
        { 0x13, "960PT" }, 
        { 0x14, "960PTL0" },
        { 0x15, "960PRL" } ,
        { 0x16, "960PTL1" },
        { 0x20, "1100PVX" },
        { -1,   NULL },
};

/*
 * Status output
 */
static void
cmd_status0(struct mlx_disk *md)
{
        int result;
 
        result = md->hwunit;
        if (ioctl(mlxfd, MLXD_STATUS, &result) < 0)
                err(EXIT_FAILURE, "ioctl(MLXD_STATUS)");

        switch(result) {
        case MLX_SYSD_ONLINE:
                printf("%s: online\n", md->name);
                break;

        case MLX_SYSD_CRITICAL:
                printf("%s: critical\n", md->name);
                if (!rstatus)
                        rstatus = 1;
                break;

        case MLX_SYSD_OFFLINE:
                printf("%s: offline\n", md->name);
                rstatus = 2;
                break;

        default:
                printf("%s: unknown status 0x%02x\n", md->name, result);
                break;
        }

        /* Rebuild/check in progress on this drive? */
        if (rs.rs_drive == md->hwunit &&
            rs.rs_code != MLX_REBUILDSTAT_IDLE) {
                switch(rs.rs_code) {
                case MLX_REBUILDSTAT_REBUILDCHECK:
                        printf(" [consistency check");
                        break;

                case MLX_REBUILDSTAT_ADDCAPACITY:
                        printf(" [add capacity");
                        break;

                case MLX_REBUILDSTAT_ADDCAPACITYINIT:
                        printf(" [add capacity init");
                        break;

                default:
                        printf(" [unknown operation");
                        break;
                }

                printf(": %d/%d, %d%% complete]\n", rs.rs_remaining, rs.rs_size,
                   ((rs.rs_size - rs.rs_remaining) / (rs.rs_size / 100)));
        }
}

int
cmd_status(char **argv)
{

        if (ioctl(mlxfd, MLX_REBUILDSTAT, &rs) < 0)
                err(EXIT_FAILURE, "ioctl(MLX_REBUILDSTAT)");

        mlx_disk_iterate(cmd_status0);
        return (rstatus);
}

/*
 * Display controller status.
 */
int
cmd_cstatus(char **argv)
{
        struct mlx_enquiry2 enq;
        struct mlx_phys_drv pd;
        static char buf[80];
        const char *model;
        int channel, target;
        size_t i;

        model = NULL;   /* XXXGCC -Wuninitialized */

        for (i = 0; i < sizeof(mlx_ctlr_names) / sizeof(mlx_ctlr_names[0]); i++)
                if (ci.ci_hardware_id == mlx_ctlr_names[i].hwid) {
                        model = mlx_ctlr_names[i].name;
                        break;
                }

        if (i == sizeof(mlx_ctlr_names) / sizeof(mlx_ctlr_names[0])) {
                snprintf(buf, sizeof(buf), " model 0x%x", ci.ci_hardware_id);
                model = buf;
        }

        printf("DAC%s, %d channel%s, firmware %d.%02d-%c-%02d",
            model, ci.ci_nchan,
            ci.ci_nchan > 1 ? "s" : "",
            ci.ci_firmware_id[0], ci.ci_firmware_id[1],
            ci.ci_firmware_id[3], ci.ci_firmware_id[2]);
        if (ci.ci_mem_size != 0)
                printf(", %dMB RAM", ci.ci_mem_size >> 20);
        printf("\n");

        if (verbosity > 0 && ci.ci_iftype > 1) {
                uint32_t hid, sid;

                mlx_enquiry(&enq);
                memcpy(&hid, enq.me_hardware_id, sizeof(hid));
                memcpy(&sid, enq.me_firmware_id, sizeof(sid));

                printf("  Hardware ID\t\t\t0x%08x\n", le32toh(hid));
                printf("  Firmware ID\t\t\t0x%08x\n", le32toh(sid));
                printf("  Configured/Actual channels\t%d/%d\n",
                    enq.me_configured_channels, enq.me_actual_channels);
                printf("  Max Targets\t\t\t%d\n", enq.me_max_targets);
                printf("  Max Tags\t\t\t%d\n", enq.me_max_tags);
                printf("  Max System Drives\t\t%d\n", enq.me_max_sys_drives);
                printf("  Max Arms\t\t\t%d\n", enq.me_max_arms);
                printf("  Max Spans\t\t\t%d\n", enq.me_max_spans);
                printf("  DRAM/cache/flash/NVRAM size\t%d/%d/%d/%d\n",
                    le32toh(enq.me_mem_size), le32toh(enq.me_cache_size),
                    le32toh(enq.me_flash_size), le32toh(enq.me_nvram_size));
                printf("  DRAM type\t\t\t%d\n", le16toh(enq.me_mem_type));
                printf("  Clock Speed\t\t\t%dns\n",
                    le16toh(enq.me_clock_speed));
                printf("  Hardware Speed\t\t%dns\n",
                    le16toh(enq.me_hardware_speed));
                printf("  Max Commands\t\t\t%d\n",
                    le16toh(enq.me_max_commands));
                printf("  Max SG Entries\t\t%d\n", le16toh(enq.me_max_sg));
                printf("  Max DP\t\t\t%d\n", le16toh(enq.me_max_dp));
                printf("  Max IOD\t\t\t%d\n", le16toh(enq.me_max_iod));
                printf("  Max Comb\t\t\t%d\n", le16toh(enq.me_max_comb));
                printf("  Latency\t\t\t%ds\n", enq.me_latency);
                printf("  SCSI Timeout\t\t\t%ds\n", enq.me_scsi_timeout);
                printf("  Min Free Lines\t\t%d\n",
                    le16toh(enq.me_min_freelines));
                printf("  Rate Constant\t\t\t%d\n", enq.me_rate_const);
                printf("  MAXBLK\t\t\t%d\n", le16toh(enq.me_maxblk));
                printf("  Blocking Factor\t\t%d sectors\n",
                    le16toh(enq.me_blocking_factor));
                printf("  Cache Line Size\t\t%d blocks\n",
                    le16toh(enq.me_cacheline));
                printf("  SCSI Capability\t\t%s%dMHz, %d bit\n", 
                      enq.me_scsi_cap & (1<<4) ? "differential " : "",
                      (1 << ((enq.me_scsi_cap >> 2) & 3)) * 10,
                      8 << (enq.me_scsi_cap & 0x3));
                printf("  Firmware Build Number\t\t%d\n",
                    le16toh(enq.me_firmware_build));
                printf("  Fault Management Type\t\t%d\n",
                    enq.me_fault_mgmt_type);
#if 0
                printf("  Features\t\t\t%b\n", enq.me_firmware_features,
                      "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n");
#endif
        } else if (verbosity > 0 && ci.ci_iftype == 1)
                warnx("can't be verbose for this firmware level");

        fflush(stdout);

        if (ci.ci_firmware_id[0] < 3) {
                warnx("can't display physical drives for this firmware level");
                return (0);
        }

        /*
         * Fetch and print physical drive data.
         */
        for (channel = 0; channel < enq.me_configured_channels; channel++) {
                for (target = 0; target < enq.me_max_targets; target++)
                        if (mlx_get_device_state(channel, target, &pd) == 0 &&
                            (pd.pd_flags1 & MLX_PHYS_DRV_PRESENT) != 0)
                                mlx_print_phys_drv(&pd, channel, target, "  ");
        }

        return (0);
}

/*
 * Recscan for new or changed system drives.
 */
int
cmd_rescan(char **argv)
{

        if (ioctl(mlxfd, MLX_RESCAN_DRIVES) < 0)
                err(EXIT_FAILURE, "rescan failed");
        return (0);
}

/*
 * Detach one or more system drives from a controller.
 */
static void
cmd_detach0(struct mlx_disk *md)
{

        if (ioctl(mlxfd, MLXD_DETACH, &md->hwunit) < 0)
                warn("can't detach %s", md->name);
}

int
cmd_detach(char **argv) 
{

        mlx_disk_iterate(cmd_detach0);
        return (0);
}

/*
 * Initiate a consistency check on a system drive.
 */
static void
cmd_check0(struct mlx_disk *md)
{
        int result;

        if (ioctl(mlxfd, MLXD_CHECKASYNC, &result) == 0)
                return;

        switch (result) {
        case 0x0002:
                warnx("one or more of the SCSI disks on which %s", md->name);
                warnx("depends is DEAD.");
                break;

        case 0x0105:
                warnx("drive %s is invalid, or not a drive which ", md->name);
                warnx("can be checked.");
                break;

        case 0x0106:
                warnx("drive rebuild or consistency check is already ");
                warnx("in progress on this controller.");
                break;

        default:
                err(EXIT_FAILURE, "ioctl(MLXD_CHECKASYNC)");
                /* NOTREACHED */
        }
}

int
cmd_check(char **argv)
{

        if (ci.ci_firmware_id[0] < 3) {
                warnx("action not supported by this firmware version");
                return (1);
        }

        mlx_disk_iterate(cmd_check0);
        return (0);
}

/*
 * Initiate a physical drive rebuild.
 */
int
cmd_rebuild(char **argv)
{
        struct mlx_rebuild_request rb;
        char *p;

        if (ci.ci_firmware_id[0] < 3) {
                warnx("action not supported by this firmware version");
                return (1);
        }

        if (argv[0] == NULL || argv[1] != NULL)
                usage();

        rb.rr_channel = (int)strtol(*argv, &p, 0);
        if (p[0] != ':' || p[1] == '\0')
                usage();

        rb.rr_target = (int)strtol(*argv, &p, 0);
        if (p[0] != '\0')
                usage();

        if (ioctl(mlxfd, MLX_REBUILDASYNC, &rb) == 0)
                return (0);

        switch (rb.rr_status) {
        case 0x0002:
                warnx("the drive at %d:%d is already ONLINE", rb.rr_channel,
                    rb.rr_target);
                break;

        case 0x0004:
                warnx("drive failed during rebuild");
                break;

        case 0x0105:
                warnx("there is no drive at %d:%d", rb.rr_channel,
                    rb.rr_target);
                break;

        case 0x0106:
                warnx("drive rebuild or consistency check is already in ");
                warnx("progress on this controller");
                break;

        default:
                err(EXIT_FAILURE, "ioctl(MLXD_CHECKASYNC)");
                /* NOTREACHED */
        }

        return(0);
}