Mercurial > illumos > onarm
diff usr/src/cmd/devctl/devctl.c @ 0:c9caec207d52 b86
Initial porting based on b86
author | Koji Uno <koji.uno@sun.com> |
---|---|
date | Tue, 02 Jun 2009 18:56:50 +0900 |
parents | |
children | 1a15d5aaf794 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/devctl/devctl.c Tue Jun 02 18:56:50 2009 +0900 @@ -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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "@(#)devctl.c 1.6 05/11/09 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}, +}; + + + +int +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 (S_ISLNK(stat_buf.st_mode)) { + 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); + } + return (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); +}