summaryrefslogtreecommitdiff
path: root/usr/src/cmd/devctl/devctl.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/devctl/devctl.c
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/devctl/devctl.c')
-rw-r--r--usr/src/cmd/devctl/devctl.c609
1 files changed, 609 insertions, 0 deletions
diff --git a/usr/src/cmd/devctl/devctl.c b/usr/src/cmd/devctl/devctl.c
new file mode 100644
index 0000000000..501515e3aa
--- /dev/null
+++ b/usr/src/cmd/devctl/devctl.c
@@ -0,0 +1,609 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+
+/*
+ * devctl - device control utility
+ *
+ * to compile:
+ * cc -o devctl -ldevice -ldevinfo devctl.c
+ *
+ * usage: devctl [-v] command [device/bus pathname]
+ *
+ * Commands:
+ * list - list all controllers exporting the devctl interface
+ * online - online a device
+ * offline - offline a device
+ * remove - remove a device from the device tree
+ * quiesce - quiesce the bus
+ * unquiesce - resume bus activity
+ * configure - configure a bus's child devices
+ * unconfigure - unconfigure a bus's child devices
+ * bus-reset - reset a bus
+ * dev-reset - reset a device
+ * bus-getstate - return the current state of the bus
+ * dev-getstate - return the current state of the device
+ * bus-devcreate - create a new device, bus specific
+ * dev-raisepower - power up a device via pm_raise_power() (pm)
+ * dev-idlecomp - idle a device's component 0 (pm)
+ * dev-busycomp - busy a device's component 0 (pm)
+ * dev-testbusy - test a device's component 0's busy state (pm)
+ * dev-changepowerhigh - power up a device via pm_power_has_changed()
+ * (pm)
+ * dev-changepowerlow - power off a device via pm_power_has_changed()
+ * (pm)
+ * dev-failsuspend - fail DDI_SUSPEND (pm)
+ * dev-changeonresume - issue pm_power_has_changed() vs,
+ * pm_raise_power() on device resume (pm)
+ * dev-nolowerpower - don't call pm_lower_power() on detach (pm)
+ * dev-promprintf - issue a prom_printf() call (pm)
+ * bus-raisepower - power up a bus via pm_raise_power() (pm)
+ * bus-idlecomp - idle a bus' component (pm)
+ * bus-busycomp - busy a bus' component (pm)
+ * bus-testbusy - test a bus' component busy state (pm)
+ * bus-changepowerhigh - power up a bus via pm_power_has_changed() (pm)
+ * bus-changepowerlow - power off a bus via pm_power_has_changed()
+ * (pm)
+ * bus-failsuspend - fail DDI_SUSPEND (pm)
+ * bus-teststrict - test is bus driver is strict or involved (pm)
+ * bus-noinvol - mark idle twice when child detaches
+ *
+ *
+ * Returns:
+ * - Success
+ * - Operation not supported by device
+ * - No Permission
+ * - No Such Device
+ *
+ * Examples:
+ * devctl list - list all controllers exporting a :devctl node
+ * devctl offline /dev/dsk/c0t3d0s0 - offline disk
+ * devctl dev-getstate /devices/sbus@1f,0/espdma@e,8400000/esp@e,8800000\
+ * sd@3,0
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <libdevice.h>
+#include <libdevinfo.h>
+#include <sys/sunddi.h>
+
+typedef struct cmds {
+ char *cmdname;
+ int (*cmdf)(devctl_hdl_t);
+} cmds_t;
+
+extern int errno;
+
+static void setprogname(char *name);
+static void print_bus_state(char *devname, uint_t state);
+static void print_dev_state(char *devname, uint_t state);
+static int dev_getstate(devctl_hdl_t);
+static int bus_getstate(devctl_hdl_t);
+static int bus_devcreate(devctl_hdl_t);
+static void run_list_ctlrs(void);
+static struct cmds *dc_cmd();
+static int nexif(di_node_t din, di_minor_t dim, void *arg);
+static void *s_malloc(size_t);
+static void *s_realloc(void *, size_t);
+static char *s_strdup(char *);
+static int dev_pm_testbusy(devctl_hdl_t);
+static int bus_pm_teststrict(devctl_hdl_t);
+
+static char *devctl_device;
+static char *orig_path;
+static char *devctl_cmdname;
+static char *progname;
+static int verbose;
+static int debug;
+static char *dev_name;
+static char **dev_props;
+
+static const char *usage = "%s [-v] list | online | offline | remove |\n"
+ "\tquiesce | unquiesce | configure | unconfigure |\n"
+ "\t{bus,dev}-reset {bus,dev}-getstate | {bus,dev}-raisepower |\n"
+ "\t{bus,dev}-idlecomp | {bus,dev}-busycomp |\n"
+ "\t{bus,dev}-changepowerhigh | {bus,dev}-changepowerlow |\n"
+ "\t{bus,dev}-testbusy | {bus,dev}-failsuspend | dev-changeonresume |\n"
+ "\tdev-promprintf | dev-nolowerpower | bus-teststrict |\n"
+ "\tbus-noinvol [/dev/... | /devices/...]\n";
+
+static struct cmds device_cmds[] = {
+ {"online", devctl_device_online},
+ {"offline", devctl_device_offline},
+ {"remove", devctl_device_remove},
+ {"dev-reset", devctl_device_reset},
+ {"dev-getstate", dev_getstate},
+ {"dev-raisepower", devctl_pm_raisepower},
+ {"dev-busycomp", devctl_pm_busycomponent},
+ {"dev-idlecomp", devctl_pm_idlecomponent},
+ {"dev-testbusy", dev_pm_testbusy},
+ {"dev-changepowerlow", devctl_pm_changepowerlow},
+ {"dev-changepowerhigh", devctl_pm_changepowerhigh},
+ {"dev-failsuspend", devctl_pm_failsuspend},
+ {"dev-changeonresume", devctl_pm_device_changeonresume},
+ {"dev-promprintf", devctl_pm_device_promprintf},
+ {"dev-nolowerpower", devctl_pm_device_no_lower_power},
+ {NULL, NULL},
+};
+
+static struct cmds bus_cmds[] = {
+ {"quiesce", devctl_bus_quiesce},
+ {"unquiesce", devctl_bus_unquiesce},
+ {"bus-reset", devctl_bus_reset},
+ {"configure", devctl_bus_configure},
+ {"unconfigure", devctl_bus_unconfigure},
+ {"bus-getstate", bus_getstate},
+ {"bus-devcreate", bus_devcreate},
+ {"bus-raisepower", devctl_pm_raisepower},
+ {"bus-busycomp", devctl_pm_busycomponent},
+ {"bus-idlecomp", devctl_pm_idlecomponent},
+ {"bus-changepowerlow", devctl_pm_changepowerlow},
+ {"bus-changepowerhigh", devctl_pm_changepowerhigh},
+ {"bus-testbusy", dev_pm_testbusy},
+ {"bus-failsuspend", devctl_pm_failsuspend},
+ {"bus-teststrict", bus_pm_teststrict},
+ {"bus-noinvol", devctl_pm_bus_no_invol},
+ {NULL, NULL},
+};
+
+
+
+void
+main(int argc, char *argv[])
+{
+ int c;
+ int rv;
+ int pathlen;
+ struct cmds *dcmd;
+ devctl_hdl_t dcp;
+ struct stat stat_buf;
+
+ setprogname(argv[0]);
+ while ((c = getopt(argc, argv, "vd")) != -1) {
+ switch (c) {
+ case 'v':
+ ++verbose;
+ break;
+ case 'd':
+ ++debug;
+ (void) putenv("LIBDEVICE_DEBUG");
+ break;
+ default:
+ (void) fprintf(stderr, usage, progname);
+ exit(1);
+ /*NOTREACHED*/
+ }
+ }
+
+ if (optind == argc) {
+ (void) fprintf(stderr, usage, progname);
+ exit(-1);
+ }
+
+ devctl_cmdname = argv[optind++];
+
+ if (strcmp(devctl_cmdname, "list") == 0) {
+ run_list_ctlrs();
+ exit(0);
+ }
+
+ /*
+ * any command other than "list" requires a device path
+ */
+ if (((optind + 1) > argc)) {
+ (void) fprintf(stderr, usage, progname);
+ exit(-1);
+ }
+
+ orig_path = s_strdup(argv[optind]);
+ devctl_device = s_malloc(MAXPATHLEN);
+ (void) strcpy(devctl_device, orig_path);
+
+ /*
+ * Additional properties follow for bus-devcreate
+ */
+ if ((optind + 1 < argc) &&
+ strcmp(devctl_cmdname, "bus-devcreate") == 0) {
+ int i;
+ optind++;
+ dev_name = s_strdup(argv[optind]);
+ i = argc - optind;
+ dev_props = s_malloc(i * sizeof (char *));
+ while (--i) {
+ dev_props[i - 1] = s_strdup(argv[optind + i]);
+ }
+ }
+
+ /*
+ * if the device is a logical name, get the physical name
+ */
+ if (lstat(orig_path, &stat_buf) == 0) {
+ if ((stat_buf.st_mode & S_IFLNK) == S_IFLNK) {
+ if ((pathlen = readlink(orig_path, devctl_device,
+ MAXPATHLEN)) == -1) {
+ (void) fprintf(stderr,
+ "devctl: readlink(%s) - %s\n",
+ orig_path, strerror(errno));
+ exit(-1);
+ }
+ devctl_device[pathlen] = '\0';
+ }
+ }
+
+ if ((dcmd = dc_cmd(device_cmds, devctl_cmdname)) == NULL) {
+ dcmd = dc_cmd(bus_cmds, devctl_cmdname);
+ if (dcmd == NULL) {
+ (void) fprintf(stderr, "unrecognized command (%s)\n",
+ devctl_cmdname);
+ (void) fprintf(stderr, usage, progname);
+ exit(1);
+ } else if (strcmp(devctl_cmdname, "bus-raisepower") == 0 ||
+ strcmp(devctl_cmdname, "bus-changepowerlow") == 0 ||
+ strcmp(devctl_cmdname, "bus-changepowerhigh") == 0 ||
+ strcmp(devctl_cmdname, "bus-idlecomp") == 0 ||
+ strcmp(devctl_cmdname, "bus-busycomp") == 0 ||
+ strcmp(devctl_cmdname, "bus-testbusy") == 0 ||
+ strcmp(devctl_cmdname, "bus-failsuspend") == 0 ||
+ strcmp(devctl_cmdname, "bus-teststrict") == 0 ||
+ strcmp(devctl_cmdname, "bus-noinvol") == 0) {
+ dcp = devctl_pm_bus_acquire(devctl_device, 0);
+ if (dcp == NULL) {
+ (void) fprintf(stderr,
+ "devctl: device_pm_bus_acquire %s - %s\n",
+ devctl_device, strerror(errno));
+ exit(-1);
+ }
+ } else {
+ dcp = devctl_bus_acquire(devctl_device, 0);
+ if (dcp == NULL) {
+ (void) fprintf(stderr, "devctl: bus_acquire "
+ "%s - %s\n",
+ devctl_device, strerror(errno));
+ exit(-1);
+ }
+ }
+ } else if (strcmp(devctl_cmdname, "dev-raisepower") == 0 ||
+ strcmp(devctl_cmdname, "dev-changepowerlow") == 0 ||
+ strcmp(devctl_cmdname, "dev-changepowerhigh") == 0 ||
+ strcmp(devctl_cmdname, "dev-idlecomp") == 0 ||
+ strcmp(devctl_cmdname, "dev-busycomp") == 0 ||
+ strcmp(devctl_cmdname, "dev-testbusy") == 0 ||
+ strcmp(devctl_cmdname, "dev-failsuspend") == 0 ||
+ strcmp(devctl_cmdname, "dev-changeonresume") == 0 ||
+ strcmp(devctl_cmdname, "dev-promprintf") == 0 ||
+ strcmp(devctl_cmdname, "dev-nolowerpower") == 0) {
+ dcp = devctl_pm_dev_acquire(devctl_device, 0);
+ if (dcp == NULL) {
+ (void) fprintf(stderr,
+ "devctl: device_pm_dev_acquire %s - %s\n",
+ devctl_device, strerror(errno));
+ exit(-1);
+ }
+ } else {
+ dcp = devctl_device_acquire(devctl_device, 0);
+ if (dcp == NULL) {
+ (void) fprintf(stderr,
+ "devctl: device_acquire %s - %s\n",
+ devctl_device, strerror(errno));
+ exit(-1);
+ }
+ }
+
+ if (verbose)
+ (void) printf("devctl: cmd (%s) device (%s)\n",
+ devctl_cmdname, orig_path);
+
+ (void) fflush(NULL); /* get output out of the way */
+
+ rv = (dcmd->cmdf)(dcp);
+
+ if (rv == -1) {
+ perror("devctl");
+ exit(-1);
+ }
+ exit(0);
+} /* main */
+
+static int
+dev_pm_testbusy(devctl_hdl_t dcp)
+{
+ int rv;
+ uint_t *busyp;
+
+ busyp = s_malloc(sizeof (uint_t));
+ rv = devctl_pm_testbusy(dcp, busyp);
+ if (rv != -1)
+ (void) printf("%s: busy state %d\n", orig_path, *busyp);
+
+ return (0);
+}
+
+static int
+bus_pm_teststrict(devctl_hdl_t dcp)
+{
+ int rv;
+ uint_t *strict;
+
+ strict = s_malloc(sizeof (uint_t));
+
+ rv = devctl_pm_bus_teststrict(dcp, strict);
+ if (rv != -1)
+ (void) printf("%s: strict %d\n", orig_path, *strict);
+
+ return (0);
+}
+
+static int
+dev_getstate(devctl_hdl_t dcp)
+{
+ int rv;
+ uint_t state;
+
+ rv = devctl_device_getstate(dcp, &state);
+ if (rv != -1)
+ print_dev_state(orig_path, state);
+
+ return (0);
+}
+
+static int
+bus_getstate(devctl_hdl_t dcp)
+{
+ int rv;
+ uint_t state;
+
+ rv = devctl_bus_getstate(dcp, &state);
+ if (rv != -1)
+ print_bus_state(orig_path, state);
+
+ return (0);
+}
+
+/*
+ * Only string property is supported now.
+ * Will add more later.
+ */
+static void
+add_prop(devctl_ddef_t ddef_hdl, char *prop_str)
+{
+ char *pname, *pval, *tmp;
+ char **strs = NULL;
+ int nstr;
+
+ tmp = strchr(prop_str, '=');
+ if (tmp == NULL) {
+ (void) fprintf(stderr, "invalid property %s", prop_str);
+ exit(-1);
+ }
+
+ (void) printf("prop string: %s\n", prop_str);
+ pname = prop_str;
+ *tmp++ = '\0';
+ if (*tmp != '"') {
+ (void) devctl_ddef_string(ddef_hdl, pname, tmp);
+ return;
+ }
+
+ nstr = 0;
+ while (*tmp != '\0') {
+ pval = tmp + 1;
+ tmp = strchr(pval, '"');
+ if (tmp == NULL) {
+ (void) fprintf(stderr, "missing quote in %s", tmp);
+ exit(-1);
+ }
+ nstr++;
+ strs = (char **)s_realloc(strs, nstr * sizeof (char *));
+ strs[nstr - 1] = pval;
+ *tmp++ = '\0';
+ (void) printf("string[%d] = %s\n", nstr - 1, pval);
+ if (*tmp)
+ tmp = strchr(tmp, '"');
+ if (tmp == NULL) {
+ (void) fprintf(stderr, "string not ending with quote");
+ exit(-1);
+ }
+ }
+
+ (void) devctl_ddef_string_array(ddef_hdl, pname, nstr, strs);
+ free(strs);
+}
+
+static int
+bus_devcreate(devctl_hdl_t bus_dcp)
+{
+ int rv;
+ char **propp = dev_props;
+ devctl_ddef_t ddef_hdl = NULL;
+ devctl_hdl_t dev_hdl = NULL;
+
+ ddef_hdl = devctl_ddef_alloc(dev_name, 0);
+ if (dev_props == NULL) {
+ (void) fprintf(stderr, "dev-create: missing device props\n");
+ return (-1);
+ }
+
+ while (*propp) {
+ add_prop(ddef_hdl, *propp);
+ propp++;
+ }
+
+ if (devctl_bus_dev_create(bus_dcp, ddef_hdl, 0, &dev_hdl)) {
+ (void) fprintf(stderr,
+ "bus-devcreate: failed to create device node\n");
+ rv = -1;
+ } else if (devctl_get_pathname(dev_hdl, devctl_device, MAXPATHLEN)
+ == NULL) {
+ (void) fprintf(stderr,
+ "bus-devcreate: failed to get device path\n");
+ rv = -1;
+ } else {
+ (void) printf("created device %s\n", devctl_device);
+ rv = 0;
+ }
+
+ devctl_ddef_free(ddef_hdl);
+ if (dev_hdl)
+ devctl_release(dev_hdl);
+
+ return (rv);
+}
+
+static void
+print_bus_state(char *devname, uint_t state)
+{
+ (void) printf("\t%s: ", devname);
+ if (state == BUS_QUIESCED)
+ (void) printf("Quiesced");
+ else if (state == BUS_ACTIVE)
+ (void) printf("Active");
+ else if (state == BUS_SHUTDOWN)
+ (void) printf("Shutdown");
+ (void) printf("\n");
+}
+
+static void
+print_dev_state(char *devname, uint_t state)
+{
+ (void) printf("\t%s: ", devname);
+ if (state & DEVICE_ONLINE) {
+ (void) printf("Online");
+ if (state & DEVICE_BUSY)
+ (void) printf(" Busy");
+ if (state & DEVICE_DOWN)
+ (void) printf(" Down");
+ } else {
+ if (state & DEVICE_OFFLINE) {
+ (void) printf("Offline");
+ if (state & DEVICE_DOWN)
+ (void) printf(" Down");
+ }
+ }
+ (void) printf("\n");
+}
+
+static void
+setprogname(char *name)
+{
+ register char *p;
+
+ if (p = strrchr(name, '/'))
+ progname = p + 1;
+ else
+ progname = name;
+}
+
+static struct cmds *
+dc_cmd(struct cmds *cmd_tbl, char *devctl_cmdname)
+{
+ int i;
+
+ for (i = 0; cmd_tbl[i].cmdname != NULL; i++) {
+ if (strcasecmp(cmd_tbl[i].cmdname, devctl_cmdname) == 0)
+ return (&cmd_tbl[i]);
+ }
+
+ return (NULL);
+}
+
+/*
+ * list all nexus drivers exporting the :devctl minor device
+ */
+static void
+run_list_ctlrs(void)
+{
+ di_node_t dinode;
+
+ if ((dinode = di_init("/", DINFOSUBTREE|DINFOMINOR)) == NULL) {
+ (void) fprintf(stderr, "%s: di_init() failed\n",
+ progname);
+ exit(-1);
+ }
+ (void) di_walk_minor(dinode, DDI_NT_NEXUS, NULL, 0, &nexif);
+ di_fini(dinode);
+ exit(0);
+}
+
+/*ARGSUSED*/
+static int
+nexif(di_node_t din, di_minor_t dim, void *arg)
+{
+ char *devname;
+
+ if ((devname = di_devfs_path(din)) != NULL) {
+ (void) printf("%s%d: /devices%s\n", di_driver_name(din),
+ di_instance(din), devname);
+ di_devfs_path_free(devname);
+ }
+
+ return (DI_WALK_CONTINUE);
+}
+
+void *
+s_malloc(size_t len)
+{
+ void *buf = malloc(len);
+
+ if (buf == NULL) {
+ perror("s_malloc failed");
+ exit(-1);
+ }
+ return (buf);
+}
+
+void *
+s_realloc(void *ptr, size_t len)
+{
+ void *buf = realloc(ptr, len);
+
+ if (buf == NULL) {
+ perror("s_realloc failed");
+ exit(-1);
+ }
+ return (buf);
+}
+
+char *
+s_strdup(char *str)
+{
+ char *buf = strdup(str);
+
+ if (buf == NULL) {
+ perror("s_malloc failed");
+ exit(-1);
+ }
+ return (buf);
+}