summaryrefslogtreecommitdiffstats
path: root/usr.sbin/mlxcontrol/command.c
diff options
context:
space:
mode:
authormsmith <msmith@FreeBSD.org>2000-04-11 03:01:45 +0000
committermsmith <msmith@FreeBSD.org>2000-04-11 03:01:45 +0000
commite9115d2df19df8d9a0db6c4e98f69f7d0b089a76 (patch)
tree3cc2441cf5076d6c3e51a83fb0ac66666d975085 /usr.sbin/mlxcontrol/command.c
parentadf731f0abaa675658ccf1c99ca1559e84334ca9 (diff)
downloadFreeBSD-src-e9115d2df19df8d9a0db6c4e98f69f7d0b089a76.zip
FreeBSD-src-e9115d2df19df8d9a0db6c4e98f69f7d0b089a76.tar.gz
This is a command-line management tool for RAID controllers managed by the
mlx(4) driver.
Diffstat (limited to 'usr.sbin/mlxcontrol/command.c')
-rw-r--r--usr.sbin/mlxcontrol/command.c717
1 files changed, 717 insertions, 0 deletions
diff --git a/usr.sbin/mlxcontrol/command.c b/usr.sbin/mlxcontrol/command.c
new file mode 100644
index 0000000..507f75a
--- /dev/null
+++ b/usr.sbin/mlxcontrol/command.c
@@ -0,0 +1,717 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+
+#if 0
+#include <sys/mlxio.h>
+#include <sys/mlxreg.h>
+#else
+#include "../sys/dev/mlx/mlxio.h"
+#include "../sys/dev/mlx/mlxreg.h"
+#endif
+
+#include "mlxcontrol.h"
+
+static int cmd_status(int argc, char *argv[]);
+static int cmd_rescan(int argc, char *argv[]);
+static int cmd_detach(int argc, char *argv[]);
+static int cmd_check(int argc, char *argv[]);
+static int cmd_rebuild(int argc, char *argv[]);
+#ifdef SUPPORT_PAUSE
+static int cmd_pause(int argc, char *argv[]);
+#endif
+static int cmd_help(int argc, char *argv[]);
+
+extern int cmd_config(int argc, char *argv[]);
+
+
+struct
+{
+ char *cmd;
+ int (*func)(int argc, char *argv[]);
+ char *desc;
+ char *text;
+} commands[] = {
+ {"status", cmd_status,
+ "displays device status",
+ " status [-qv] [<drive>...]\n"
+ " Display status for <drive> or all drives if none is listed\n"
+ " -q Suppress output.\n"
+ " -v Display verbose information.\n"
+ " Returns 0 if all drives tested are online, 1 if one or more are\n"
+ " critical, and 2 if one or more are offline."},
+ {"rescan", cmd_rescan,
+ "scan for new system drives",
+ " rescan <controller> [<controller>...]\n"
+ " Rescan <controller> for system drives.\n"
+ " rescan -a\n"
+ " Rescan all controllers for system drives."},
+ {"detach", cmd_detach,
+ "detach system drives",
+ " detach <drive> [<drive>...]\n"
+ " Detaches <drive> from the controller.\n"
+ " detach -a <controller>\n"
+ " Detaches all drives on <controller>."},
+ {"check", cmd_check,
+ "consistency-check a system drive",
+ " check <drive>\n"
+ " Requests a check and rebuild of the parity information on <drive>.\n"
+ " Note that each controller can only check one system drive at a time."},
+ {"rebuild", cmd_rebuild,
+ "initiate a rebuild of a dead physical drive",
+ " rebuild <controller> <physdrive>\n"
+ " All system drives using space on the physical drive <physdrive>\n"
+ " are rebuilt, reconstructing all data on the drive.\n"
+ " Note that each controller can only perform one rebuild at a time."},
+#ifdef SUPPORT_PAUSE
+ {"pause", cmd_pause,
+ "pauses controller channels",
+ " pause [-t <howlong>] [-d <delay>] <controller> [<channel>...]\n"
+ " Pauses SCSI I/O on <channel> and <controller>. If no channel is specified,\n"
+ " all channels are paused.\n"
+ " <howlong> How long (seconds) to pause for (default 30).\n"
+ " <delay> How long (seconds) to wait before pausing (default 30).\n"
+ " pause <controller> -c\n"
+ " Cancels any pending pause operation on <controller>."},
+#endif
+ {"config", cmd_config,
+ "examine and update controller configuration",
+ " config <controller>\n"
+ " Print configuration for <controller>."},
+ {"help", cmd_help,
+ "give help on usage",
+ ""},
+ {NULL, NULL, NULL, NULL}
+};
+
+/********************************************************************************
+ * Command dispatch and global options parsing.
+ */
+
+int
+main(int argc, char *argv[])
+{
+ int ch, i, oargc;
+ char **oargv;
+
+ oargc = argc;
+ oargv = argv;
+ while ((ch = getopt(argc, argv, "")) != -1)
+ switch(ch) {
+ default:
+ return(cmd_help(0, NULL));
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0)
+ for (i = 0; commands[i].cmd != NULL; i++)
+ if (!strcmp(argv[0], commands[i].cmd))
+ return(commands[i].func(argc, argv));
+
+ return(cmd_help(oargc, oargv));
+}
+
+/********************************************************************************
+ * Helptext output
+ */
+static int
+cmd_help(int argc, char *argv[])
+{
+ int i;
+
+ if (argc > 1)
+ for (i = 0; commands[i].cmd != NULL; i++)
+ if (!strcmp(argv[1], commands[i].cmd)) {
+ fprintf(stderr, "%s\n", commands[i].text);
+ fflush(stderr);
+ return(0);
+ }
+
+ if (argv != NULL)
+ fprintf(stderr, "Unknown command '%s'.\n", argv[1]);
+ fprintf(stderr, "Valid commands are:\n");
+ for (i = 0; commands[i].cmd != NULL; i++)
+ fprintf(stderr, " %-20s %s\n", commands[i].cmd, commands[i].desc);
+ fflush(stderr);
+ return(0);
+}
+
+/********************************************************************************
+ * Status output
+ *
+ * status [-qv] [<device> ...]
+ * Prints status for <device>, or all if none listed.
+ *
+ * -q Suppresses output, command returns 0 if devices are OK, 1 if one or
+ * more devices are critical, 2 if one or more devices are offline.
+ */
+static struct mlx_rebuild_status rs;
+static int rs_ctrlr = -1;
+static int status_result = 0;
+
+/* XXX more verbosity! */
+static void
+status_print(int unit, void *arg)
+{
+ int verbosity = *(int *)arg;
+ int fd, result, ctrlr, sysdrive, statvalid;
+
+ /* Find which controller and what system drive we are */
+ statvalid = 0;
+ if (mlxd_find_ctrlr(unit, &ctrlr, &sysdrive)) {
+ warnx("couldn't get controller/drive for %s", drivepath(unit));
+ } else {
+ /* If we don't have rebuild stats for this controller, get them */
+ if (rs_ctrlr == ctrlr) {
+ statvalid = 1;
+ } else {
+ if ((fd = open(ctrlrpath(ctrlr), 0)) < 0) {
+ warn("can't open %s", ctrlrpath(ctrlr));
+ } else {
+ if (ioctl(fd, MLX_REBUILDSTAT, &rs) < 0) {
+ warn("ioctl MLX_REBUILDSTAT");
+ } else {
+ rs_ctrlr = ctrlr;
+ statvalid = 1;
+ }
+ close(fd);
+ }
+ }
+ }
+
+ /* Get the device */
+ if ((fd = open(drivepath(unit), 0)) < 0) {
+ warn("can't open %s", drivepath(unit));
+ return;
+ }
+
+ /* Get its status */
+ if (ioctl(fd, MLXD_STATUS, &result) < 0) {
+ warn("ioctl MLXD_STATUS");
+ } else {
+ switch(result) {
+ case MLX_SYSD_ONLINE:
+ if (verbosity > 0)
+ printf("%s: online", drivename(unit));
+ break;
+ case MLX_SYSD_CRITICAL:
+ if (verbosity > 0)
+ printf("%s: critical", drivename(unit));
+ if (status_result < 1)
+ status_result = 1;
+ break;
+ case MLX_SYSD_OFFLINE:
+ if (verbosity > 0)
+ printf("%s: offline", drivename(unit));
+ if (status_result < 2)
+ status_result = 2;
+ break;
+ default:
+ if (verbosity > 0) {
+ printf("%s: unknown status 0x%x", drivename(unit), result);
+ }
+ }
+ if (verbosity > 0) {
+ /* rebuild/check in progress on this drive? */
+ if (statvalid && (rs_ctrlr == ctrlr) &&
+ (rs.rs_drive == sysdrive) && (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");
+ }
+ printf(": %d/%d, %d%% complete]",
+ rs.rs_remaining, rs.rs_size,
+ ((rs.rs_size - rs.rs_remaining) / (rs.rs_size / 100)));
+ }
+ printf("\n");
+ }
+ }
+ close(fd);
+}
+
+static struct
+{
+ int hwid;
+ char *name;
+} mlx_controller_names[] = {
+ {0x01, "960P/PD"},
+ {0x02, "960PL"},
+ {0x10, "960PG"},
+ {0x11, "960PJ"},
+ {0x12, "960PR"},
+ {0x13, "960PT"},
+ {0x14, "960PTL0"},
+ {0x15, "960PRL"},
+ {0x16, "960PTL1"},
+ {0x20, "1100PVX"},
+ {-1, NULL}
+};
+
+static void
+controller_print(int unit, void *arg)
+{
+ struct mlx_enquiry2 enq;
+ struct mlx_phys_drv pd;
+ int verbosity = *(int *)arg;
+ static char buf[80];
+ char *model;
+ int i, channel, target;
+
+ if (verbosity == 0)
+ return;
+
+ /* fetch and print controller data */
+ if (mlx_enquiry(unit, &enq)) {
+ printf("mlx%d: error submitting ENQUIRY2\n", unit);
+ } else {
+
+ for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) {
+ if ((enq.me_hardware_id & 0xff) == mlx_controller_names[i].hwid) {
+ model = mlx_controller_names[i].name;
+ break;
+ }
+ }
+ if (model == NULL) {
+ sprintf(buf, " model 0x%x", enq.me_hardware_id & 0xff);
+ model = buf;
+ }
+
+ printf("mlx%d: DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n",
+ unit, model,
+ enq.me_actual_channels,
+ enq.me_actual_channels > 1 ? "s" : "",
+ enq.me_firmware_id & 0xff,
+ (enq.me_firmware_id >> 8) & 0xff,
+ (enq.me_firmware_id >> 16),
+ (enq.me_firmware_id >> 24) & 0xff,
+ enq.me_mem_size / (1024 * 1024));
+
+ if (verbosity > 1) {
+ printf(" Hardware ID 0x%08x\n", enq.me_hardware_id);
+ printf(" Firmware ID 0x%08x\n", enq.me_firmware_id);
+ printf(" Configured/Actual channels %d/%d\n", enq.me_configured_channels,
+ enq.me_actual_channels);
+ printf(" Max Targets %d\n", enq.me_max_targets);
+ printf(" Max Tags %d\n", enq.me_max_tags);
+ printf(" Max System Drives %d\n", enq.me_max_sys_drives);
+ printf(" Max Arms %d\n", enq.me_max_arms);
+ printf(" Max Spans %d\n", enq.me_max_spans);
+ printf(" DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", enq.me_mem_size,
+ enq.me_cache_size, enq.me_flash_size, enq.me_nvram_size);
+ printf(" DRAM type %d\n", enq.me_mem_type);
+ printf(" Clock Speed %dns\n", enq.me_clock_speed);
+ printf(" Hardware Speed %dns\n", enq.me_hardware_speed);
+ printf(" Max Commands %d\n", enq.me_max_commands);
+ printf(" Max SG Entries %d\n", enq.me_max_sg);
+ printf(" Max DP %d\n", enq.me_max_dp);
+ printf(" Max IOD %d\n", enq.me_max_iod);
+ printf(" Max Comb %d\n", enq.me_max_comb);
+ printf(" Latency %ds\n", enq.me_latency);
+ printf(" SCSI Timeout %ds\n", enq.me_scsi_timeout);
+ printf(" Min Free Lines %d\n", enq.me_min_freelines);
+ printf(" Rate Constant %d\n", enq.me_rate_const);
+ printf(" MAXBLK %d\n", enq.me_maxblk);
+ printf(" Blocking Factor %d sectors\n", enq.me_blocking_factor);
+ printf(" Cache Line Size %d blocks\n", enq.me_cacheline);
+ printf(" SCSI Capability %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 %d\n", enq.me_firmware_build);
+ printf(" Fault Management Type %d\n", enq.me_fault_mgmt_type);
+#if 0
+ printf(" Features %b\n", enq.me_firmware_features,
+ "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n");
+#endif
+ }
+
+ /* 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(unit, channel, target, &pd) == 0) &&
+ (pd.pd_flags1 & MLX_PHYS_DRV_PRESENT)) {
+ mlx_print_phys_drv(&pd, channel, target, " ", verbosity - 1);
+ if (verbosity > 1) {
+ /* XXX print device statistics? */
+ }
+ }
+ }
+ }
+ }
+}
+
+static int
+cmd_status(int argc, char *argv[])
+{
+ int ch, verbosity = 1, i, unit;
+
+ optreset = 1;
+ optind = 1;
+ while ((ch = getopt(argc, argv, "qv")) != -1)
+ switch(ch) {
+ case 'q':
+ verbosity = 0;
+ break;
+ case 'v':
+ verbosity = 2;
+ break;
+ default:
+ return(cmd_help(argc, argv));
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ mlx_foreach(controller_print, &verbosity);
+ mlxd_foreach(status_print, &verbosity);
+ } else {
+ for (i = 0; i < argc; i++) {
+ if ((unit = driveunit(argv[i])) == -1) {
+ warnx("'%s' is not a valid drive", argv[i]);
+ } else {
+ status_print(unit, &verbosity);
+ }
+ }
+ }
+ return(status_result);
+}
+
+/********************************************************************************
+ * Recscan for system drives on one or more controllers.
+ *
+ * rescan <controller> [<controller>...]
+ * rescan -a
+ */
+static void
+rescan_ctrlr(int unit, void *junk)
+{
+ int fd;
+
+ /* Get the device */
+ if ((fd = open(ctrlrpath(unit), 0)) < 0) {
+ warn("can't open %s", ctrlrpath(unit));
+ return;
+ }
+
+ if (ioctl(fd, MLX_RESCAN_DRIVES) < 0)
+ warn("can't rescan %s", ctrlrname(unit));
+ close(fd);
+}
+
+static int
+cmd_rescan(int argc, char *argv[])
+{
+ int all = 0, i, ch, unit;
+
+ optreset = 1;
+ optind = 1;
+ while ((ch = getopt(argc, argv, "a")) != -1)
+ switch(ch) {
+ case 'a':
+ all = 1;
+ break;
+ default:
+ return(cmd_help(argc, argv));
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (all) {
+ mlx_foreach(rescan_ctrlr, NULL);
+ } else {
+ for (i = 0; i < argc; i++) {
+ if ((unit = ctrlrunit(argv[i])) == -1) {
+ warnx("'%s' is not a valid controller", argv[i]);
+ } else {
+ rescan_ctrlr(unit, NULL);
+ }
+ }
+ }
+ return(0);
+}
+
+/********************************************************************************
+ * Detach one or more system drives from a controller.
+ *
+ * detach <drive> [<drive>...]
+ * Detach <drive>.
+ *
+ * detach -a <controller> [<controller>...]
+ * Detach all drives on <controller>.
+ *
+ */
+static void
+detach_drive(int unit, void *arg)
+{
+ int fd;
+
+ /* Get the device */
+ if ((fd = open(ctrlrpath(unit), 0)) < 0) {
+ warn("can't open %s", ctrlrpath(unit));
+ return;
+ }
+
+ if (ioctl(fd, MLX_DETACH_DRIVE, &unit) < 0)
+ warn("can't detach %s", drivename(unit));
+ close(fd);
+}
+
+static int
+cmd_detach(int argc, char *argv[])
+{
+ struct mlxd_foreach_action ma;
+ int all = 0, i, ch, unit;
+
+ optreset = 1;
+ optind = 1;
+ while ((ch = getopt(argc, argv, "a")) != -1)
+ switch(ch) {
+ case 'a':
+ all = 1;
+ break;
+ default:
+ return(cmd_help(argc, argv));
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (all) {
+ ma.func = detach_drive;
+ ma.arg = &unit;
+ for (i = 0; i < argc; i++) {
+ if ((unit = ctrlrunit(argv[i])) == -1) {
+ warnx("'%s' is not a valid controller", argv[i]);
+ } else {
+ mlxd_foreach_ctrlr(unit, &ma);
+ }
+ }
+ } else {
+ for (i = 0; i < argc; i++) {
+ if ((unit = driveunit(argv[i])) == -1) {
+ warnx("'%s' is not a valid drive", argv[i]);
+ } else {
+ /* run across all controllers to find this drive */
+ mlx_foreach(detach_drive, &unit);
+ }
+ }
+ }
+ return(0);
+}
+
+/********************************************************************************
+ * Initiate a consistency check on a system drive.
+ *
+ * check [<drive>]
+ * Start a check of <drive>
+ *
+ */
+static int
+cmd_check(int argc, char *argv[])
+{
+ int unit, fd, result;
+
+ if (argc != 2)
+ return(cmd_help(argc, argv));
+
+ if ((unit = driveunit(argv[1])) == -1) {
+ warnx("'%s' is not a valid drive", argv[1]);
+ } else {
+
+ /* Get the device */
+ if ((fd = open(drivepath(unit), 0)) < 0) {
+ warn("can't open %s", drivepath(unit));
+ } else {
+ /* Try to start the check */
+ if ((ioctl(fd, MLXD_CHECKASYNC, &result)) < 0) {
+ switch(result) {
+ case 0x0002:
+ warnx("one or more of the SCSI disks on which the drive '%s' depends is DEAD", argv[1]);
+ break;
+ case 0x0105:
+ warnx("drive %s is invalid, or not a drive which can be checked", argv[1]);
+ break;
+ case 0x0106:
+ warnx("drive rebuild or consistency check is already in progress on this controller");
+ break;
+ default:
+ warn("ioctl MLXD_CHECKASYNC");
+ }
+ }
+ }
+ }
+ return(0);
+}
+
+/********************************************************************************
+ * Initiate a physical drive rebuild
+ *
+ * rebuild <controller> <channel>:<target>
+ * Start a rebuild of <controller>:<channel>:<target>
+ *
+ */
+static int
+cmd_rebuild(int argc, char *argv[])
+{
+ struct mlx_rebuild_request rb;
+ int unit, fd;
+
+ if (argc != 3)
+ return(cmd_help(argc, argv));
+
+ /* parse arguments */
+ if ((unit = ctrlrunit(argv[1])) == -1) {
+ warnx("'%s' is not a valid controller", argv[1]);
+ return(1);
+ }
+ /* try diskXXXX and unknownXXXX as we report the latter for a dead drive ... */
+ if ((sscanf(argv[2], "disk%2d%2d", &rb.rr_channel, &rb.rr_target) != 2) &&
+ (sscanf(argv[2], "unknown%2d%2d", &rb.rr_channel, &rb.rr_target) != 2)) {
+ warnx("'%s' is not a valid physical drive", argv[2]);
+ return(1);
+ }
+ /* get the device */
+ if ((fd = open(ctrlrpath(unit), 0)) < 0) {
+ warn("can't open %s", ctrlrpath(unit));
+ return(1);
+ }
+ /* try to start the rebuild */
+ if ((ioctl(fd, MLX_REBUILDASYNC, &rb)) < 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 channel %d, target %d", rb.rr_channel, rb.rr_target);
+ break;
+ case 0x0106:
+ warnx("drive rebuild or consistency check is already in progress on this controller");
+ break;
+ default:
+ warn("ioctl MLXD_CHECKASYNC");
+ }
+ }
+ return(0);
+}
+
+#ifdef SUPPORT_PAUSE
+/********************************************************************************
+ * Pause one or more channels on a controller
+ *
+ * pause [-d <delay>] [-t <time>] <controller> [<channel>...]
+ * Pauses <channel> (or all channels) for <time> seconds after a
+ * delay of <delay> seconds.
+ * pause <controller> -c
+ * Cancels pending pause
+ */
+static int
+cmd_pause(int argc, char *argv[])
+{
+ struct mlx_pause mp;
+ int unit, i, ch, fd, cancel = 0;
+ char *cp;
+ int oargc = argc;
+ char **oargv = argv;
+
+ mp.mp_which = 0;
+ mp.mp_when = 30;
+ mp.mp_howlong = 30;
+ optreset = 1;
+ optind = 1;
+ while ((ch = getopt(argc, argv, "cd:t:")) != -1)
+ switch(ch) {
+ case 'c':
+ cancel = 1;
+ break;
+ case 'd':
+ mp.mp_when = strtol(optarg, &cp, 0);
+ if (*cp != 0)
+ return(cmd_help(argc, argv));
+ break;
+ case 't':
+ mp.mp_howlong = strtol(optarg, &cp, 0);
+ if (*cp != 0)
+ return(cmd_help(argc, argv));
+ break;
+ default:
+ return(cmd_help(argc, argv));
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* get controller unit number that we're working on */
+ if ((argc < 1) || ((unit = ctrlrunit(argv[0])) == -1))
+ return(cmd_help(oargc, oargv));
+
+ /* Get the device */
+ if ((fd = open(ctrlrpath(unit), 0)) < 0) {
+ warn("can't open %s", ctrlrpath(unit));
+ return(1);
+ }
+
+ if (argc == 1) {
+ /* controller-wide pause/cancel */
+ mp.mp_which = cancel ? MLX_PAUSE_CANCEL : MLX_PAUSE_ALL;
+ } else {
+ for (i = 1; i < argc; i++) {
+ ch = strtol(argv[i], &cp, 0);
+ if (*cp != 0) {
+ warnx("bad channel number '%s'", argv[i]);
+ continue;
+ } else {
+ mp.mp_which |= (1 << ch);
+ }
+ }
+ }
+ if ((ioctl(fd, MLX_PAUSE_CHANNEL, &mp)) < 0)
+ warn("couldn't %s %s", cancel ? "cancel pause on" : "pause", ctrlrname(unit));
+ close(fd);
+ return(0);
+}
+#endif /* SUPPORT_PAUSE */
+
OpenPOWER on IntegriCloud