diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2013-05-09 05:42:50 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2013-05-09 05:42:50 +0400 |
commit | eb25c56979283fb7b38dcabb65a694747fb15463 (patch) | |
tree | 56cb6da26f352137b71a9cde090cbe41aebee344 | |
download | libdevice-eb25c56979283fb7b38dcabb65a694747fb15463.tar.gz |
illumos libdevice sourcesillumos
-rw-r--r-- | usr/src/lib/libdevice/devctl.c | 1380 | ||||
-rw-r--r-- | usr/src/lib/libdevice/libdevice.h | 189 |
2 files changed, 1569 insertions, 0 deletions
diff --git a/usr/src/lib/libdevice/devctl.c b/usr/src/lib/libdevice/devctl.c new file mode 100644 index 0000000..da48342 --- /dev/null +++ b/usr/src/lib/libdevice/devctl.c @@ -0,0 +1,1380 @@ +/* + * 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" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <string.h> +#include <strings.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/nvpair.h> +#include "libdevice.h" + +static int _libdevice_debug = 0; +static const char *devctl_minorname = ":devctl"; +static const char *nullptr = "<null>"; +static const char *devctl_target_raw = "a,raw"; + +typedef enum { DEVCTL_BUS, DEVCTL_DEVICE, DEVCTL_AP, DEVCTL_CLONE, + DEVCTL_PM_DEV, DEVCTL_PM_BUS } dc_type_t; + +/* + * devctl_hdl structures are allocated by the devctl_XX_acquire() + * interfaces and passed to the remaining interfaces in this library. + */ +struct devctl_hdl { + char *opath; /* copy of the original path */ + dc_type_t hdltype; /* handle type */ + int fd; /* nexus device node */ + char *nodename; /* DEVCTL_DEVICE handles only */ + char *unitaddr; /* DEVCTL_DEVICE handles only */ +}; +#define DCP(x) ((struct devctl_hdl *)(x)) + +static int dc_cmd(uint_t, uint_t, struct devctl_hdl *, nvlist_t *, void *); +static devctl_hdl_t dc_mkhndl(dc_type_t, char *, uint_t, devctl_hdl_t); + + +#pragma init(_libdevice_init) +void +_libdevice_init() +{ + _libdevice_debug = getenv("LIBDEVICE_DEBUG") != NULL; +} + +/* + * release a devctl_hdl structure + */ +void +devctl_release(devctl_hdl_t hdl) +{ + if (_libdevice_debug) + (void) printf("devctl_release: %p\n", (void *)hdl); + + if (hdl == NULL) + return; + + if (DCP(hdl)->fd != -1) + (void) close(DCP(hdl)->fd); + + if (DCP(hdl)->opath != NULL) + free(DCP(hdl)->opath); + + if (DCP(hdl)->nodename != NULL) + free(DCP(hdl)->nodename); + + if (DCP(hdl)->unitaddr != NULL) + free(DCP(hdl)->unitaddr); + + free(hdl); +} + +/* + * construct a handle suitable for devctl_bus_*() operations + */ +devctl_hdl_t +devctl_bus_acquire(char *devfs_path, uint_t flags) +{ + uint_t oflags; + + if (_libdevice_debug) + (void) printf("devctl_bus_acquire: %s (%d)\n", + ((devfs_path != NULL) ? devfs_path : nullptr), flags); + + if ((devfs_path == NULL) || ((flags != 0) && (flags != DC_EXCL))) { + errno = EINVAL; + return (NULL); + } + + oflags = ((flags & DC_EXCL) != 0) ? O_EXCL|O_RDWR : O_RDWR; + return (dc_mkhndl(DEVCTL_BUS, devfs_path, oflags, NULL)); +} + + +/* + * construct a handle suitable for devctl_bus_*() and + * devctl_device_*() operations. + */ +devctl_hdl_t +devctl_device_acquire(char *devfs_path, uint_t flags) +{ + uint_t oflags; + + if (_libdevice_debug) + (void) printf("devctl_device_acquire: %s (%d)\n", + ((devfs_path != NULL) ? devfs_path : nullptr), flags); + + if ((devfs_path == NULL) || ((flags != 0) && (flags != DC_EXCL))) { + errno = EINVAL; + return (NULL); + } + + oflags = ((flags & DC_EXCL) != 0) ? O_EXCL|O_RDWR : O_RDWR; + return (dc_mkhndl(DEVCTL_DEVICE, devfs_path, oflags, NULL)); +} + + +/* + * given a devfs (/devices) pathname to an attachment point device, + * access the device and return a handle to be passed to the + * devctl_ap_XXX() functions. + */ +devctl_hdl_t +devctl_ap_acquire(char *devfs_path, uint_t flags) +{ + uint_t oflags; + + if (_libdevice_debug) + (void) printf("devctl_ap_acquire: %s (%d)\n", + ((devfs_path != NULL) ? devfs_path : nullptr), flags); + + if ((devfs_path == NULL) || + ((flags != 0) && ((flags & DC_EXCL) != 0) && + ((flags & DC_RDONLY) != 0))) { + errno = EINVAL; + return (NULL); + } + + oflags = ((flags & DC_EXCL) != 0) ? O_EXCL : 0; + oflags |= ((flags & DC_RDONLY) != 0) ? O_RDONLY : O_RDWR; + + return (dc_mkhndl(DEVCTL_AP, devfs_path, oflags, NULL)); +} + + +/* + * given a devfs (/devices) pathname access the device and return + * a handle to be passed to the devctl_pm_XXX() functions. + * The minor name ":devctl" is appended. + */ +devctl_hdl_t +devctl_pm_bus_acquire(char *devfs_path, uint_t flags) +{ + uint_t oflags; + + if (_libdevice_debug) + (void) printf("devctl_pm_bus_acquire: %s (%d)\n", + ((devfs_path != NULL) ? devfs_path : nullptr), flags); + + if ((devfs_path == NULL) || ((flags != 0) && (flags != DC_EXCL))) { + errno = EINVAL; + return (NULL); + } + + oflags = ((flags & DC_EXCL) != 0) ? (O_EXCL | O_RDWR) : O_RDWR; + return (dc_mkhndl(DEVCTL_PM_BUS, devfs_path, oflags, NULL)); +} + + +/* + * given a devfs (/devices) pathname access the device and return + * a handle to be passed to the devctl_pm_XXX() functions. + * The minor name is derived from the device name. + */ +devctl_hdl_t +devctl_pm_dev_acquire(char *devfs_path, uint_t flags) +{ + uint_t oflags; + + if (_libdevice_debug) + (void) printf("devctl_pm_dev_acquire: %s (%d)\n", + ((devfs_path != NULL) ? devfs_path : nullptr), flags); + + if ((devfs_path == NULL) || ((flags != 0) && (flags != DC_EXCL))) { + errno = EINVAL; + return (NULL); + } + + oflags = ((flags & DC_EXCL) != 0) ? (O_EXCL | O_RDWR) : O_RDWR; + return (dc_mkhndl(DEVCTL_PM_DEV, devfs_path, oflags, NULL)); +} + + +/* + * allocate and initalize the devctl_hdl structure for the + * particular handle type. + */ +static devctl_hdl_t +dc_mkhndl(dc_type_t type, char *path, uint_t oflags, devctl_hdl_t pc) +{ + struct devctl_hdl *dcp; + struct stat sb; + char iocpath[MAXPATHLEN]; + char *nodename, *unitsep, *minorsep, *chop; + char *minorname; + size_t strlcpy_size; + char *iocpath_dup; + char *tok; + + if ((path == NULL) || (strlen(path) > MAXPATHLEN - 1)) { + errno = EINVAL; + return (NULL); + } + + /* + * allocate handle and make a copy of the original path + */ + if ((dcp = calloc(1, sizeof (*dcp))) == NULL) { + errno = ENOMEM; + return (NULL); + } + if ((dcp->opath = strdup(path)) == NULL) { + devctl_release((devctl_hdl_t)dcp); + errno = ENOMEM; + return (NULL); + } + + (void) strcpy(iocpath, path); + dcp->hdltype = type; + dcp->fd = -1; + + /* + * break apart the pathname according to the type handle + */ + switch (type) { + case DEVCTL_PM_BUS: + /* + * chop off any minor name and concatenate the + * ":devctl" minor node name string. + */ + if ((chop = strrchr(iocpath, ':')) != NULL) + *chop = '\0'; + + if (strlcat(iocpath, devctl_minorname, MAXPATHLEN) >= + MAXPATHLEN) { + devctl_release((devctl_hdl_t)dcp); + errno = EINVAL; + return (NULL); + } else if (_libdevice_debug) { + (void) printf("DEVCTL_PM_BUS: iocpath %s\n", iocpath); + } + break; + + case DEVCTL_PM_DEV: + /* + * Chop up the last device component in the pathname. + * Concatenate either the device name itself, or the + * "a,raw" string, as the minor node name, to the iocpath. + */ + if ((iocpath_dup = strdup(iocpath)) == NULL) { + devctl_release((devctl_hdl_t)dcp); + errno = ENOMEM; + return (NULL); + } + if ((chop = strrchr(iocpath_dup, '/')) == NULL) { + devctl_release((devctl_hdl_t)dcp); + errno = EINVAL; + return (NULL); + } + *chop = '\0'; + nodename = chop + 1; + + /* + * remove the "@0,0" string + */ + tok = strtok(nodename, "@"); + if ((minorname = malloc(strlen(tok) +1)) == NULL) { + if (_libdevice_debug) + (void) printf("DEVCTL_PM_DEV: failed malloc for" + " minorname\n"); + devctl_release((devctl_hdl_t)dcp); + errno = ENOMEM; + return (NULL); + } + (void) strcpy(minorname, tok); + if (_libdevice_debug) { + (void) printf("DEVCTL_PM_DEV: minorname %s\n", + minorname); + } + + /* + * construct the name of the ioctl device + * by concatenating either ":a,raw" or ":"minorname + */ + (void) strlcat(iocpath, ":", MAXPATHLEN); + if (strcmp(minorname, "disk_chan") == 0 || + strcmp(minorname, "disk_wwn") == 0 || + strcmp(minorname, "disk_cdrom") == 0) { + strlcpy_size = strlcat(iocpath, devctl_target_raw, + MAXPATHLEN); + } else { + strlcpy_size = strlcat(iocpath, minorname, MAXPATHLEN); + } + if (strlcpy_size >= MAXPATHLEN) { + devctl_release((devctl_hdl_t)dcp); + errno = EINVAL; + return (NULL); + } else if (_libdevice_debug) { + (void) printf("DEVCTL_PM_DEV: iocpath %s\n", + iocpath); + } + break; + + case DEVCTL_AP: + /* + * take the pathname as provided. + */ + break; + + case DEVCTL_BUS: + /* + * chop off any minor name and concatenate the + * ":devctl" minor node name string. + */ + if ((chop = strrchr(iocpath, ':')) != NULL) + *chop = '\0'; + + if (strlcat(iocpath, devctl_minorname, MAXPATHLEN) >= + MAXPATHLEN) { + devctl_release((devctl_hdl_t)dcp); + errno = EINVAL; + return (NULL); + } + break; + + case DEVCTL_CLONE: + /* + * create a device handle for a new device created + * from a call to devctl_bus_dev_create() + */ + dcp->hdltype = DEVCTL_DEVICE; + + /* FALLTHRU */ + + case DEVCTL_DEVICE: + + /* + * Chop up the last device component in the pathname. + * The componets are passed as nodename and unitaddr + * in the IOCTL data for DEVCTL ops on devices. + */ + if ((chop = strrchr(iocpath, '/')) == NULL) { + devctl_release((devctl_hdl_t)dcp); + errno = EINVAL; + return (NULL); + } + *chop = '\0'; + + nodename = chop + 1; + unitsep = strchr(nodename, '@'); + minorsep = strchr(nodename, ':'); + + if (unitsep == NULL) { + devctl_release((devctl_hdl_t)dcp); + errno = EINVAL; + return (NULL); + } + + /* + * copy the nodename and unit address + */ + if (((dcp->nodename = malloc(MAXNAMELEN)) == NULL) || + ((dcp->unitaddr = malloc(MAXNAMELEN)) == NULL)) { + devctl_release((devctl_hdl_t)dcp); + errno = ENOMEM; + return (NULL); + } + *unitsep = '\0'; + if (minorsep != NULL) + *minorsep = '\0'; + (void) snprintf(dcp->nodename, MAXNAMELEN, "%s", nodename); + (void) snprintf(dcp->unitaddr, MAXNAMELEN, "%s", unitsep+1); + + /* + * construct the name of the ioctl device + */ + if (strlcat(iocpath, devctl_minorname, MAXPATHLEN) >= + MAXPATHLEN) { + devctl_release((devctl_hdl_t)dcp); + errno = EINVAL; + return (NULL); + } + break; + + default: + devctl_release((devctl_hdl_t)dcp); + errno = EINVAL; + return (NULL); + } + + if (_libdevice_debug) + (void) printf("dc_mkhndl: iocpath %s ", iocpath); + + /* + * verify the devctl or ap device exists and is a + * character device interface. + */ + if (stat(iocpath, &sb) == 0) { + if ((sb.st_mode & S_IFMT) != S_IFCHR) { + if (_libdevice_debug) + (void) printf(" - not character device\n"); + errno = ENODEV; + devctl_release((devctl_hdl_t)dcp); + return (NULL); + } + } else { + /* + * return failure with errno value set by stat + */ + if (_libdevice_debug) + (void) printf(" - stat failed\n"); + devctl_release((devctl_hdl_t)dcp); + return (NULL); + } + + /* + * if this was a new device, dup the parents handle, otherwise + * just open the device. + */ + if (type == DEVCTL_CLONE) + dcp->fd = dup(DCP(pc)->fd); + else + dcp->fd = open(iocpath, oflags); + + if (dcp->fd == -1) { + if (_libdevice_debug) + (void) printf(" - open/dup failed %d\n", errno); + /* + * leave errno as set by open/dup + */ + devctl_release((devctl_hdl_t)dcp); + return (NULL); + } + + if (_libdevice_debug) + (void) printf(" - open success\n"); + + return ((devctl_hdl_t)dcp); +} + +/* + * Power up component 0, to level MAXPWR, via a pm_raise_power() call + */ +int +devctl_pm_raisepower(devctl_hdl_t dcp) +{ + int rv; + + if (dcp == NULL || (DCP(dcp)->hdltype != DEVCTL_PM_DEV && + DCP(dcp)->hdltype != DEVCTL_PM_BUS)) { + errno = EINVAL; + return (-1); + } + + rv = dc_cmd(DEVCTL_PM_RAISE_PWR, 0, DCP(dcp), NULL, NULL); + + if (_libdevice_debug) + (void) printf("devctl_pm_raisepower: %d\n", rv); + + return (rv); +} + +/* + * Power up component 0, to level MAXPWR, via a power_has_changed() call + */ +int +devctl_pm_changepowerhigh(devctl_hdl_t dcp) +{ + int rv; + + if (dcp == NULL || (DCP(dcp)->hdltype != DEVCTL_PM_DEV && + DCP(dcp)->hdltype != DEVCTL_PM_BUS)) { + errno = EINVAL; + return (-1); + } + + rv = dc_cmd(DEVCTL_PM_CHANGE_PWR_HIGH, 0, DCP(dcp), NULL, NULL); + + if (_libdevice_debug) + (void) printf("devctl_pm_changepowerhigh: %d\n", rv); + + return (rv); +} + +/* + * Power down component 0, to level 0, via a pm_change_power() call + */ +int +devctl_pm_changepowerlow(devctl_hdl_t dcp) +{ + int rv; + + if (dcp == NULL || (DCP(dcp)->hdltype != DEVCTL_PM_DEV && + DCP(dcp)->hdltype != DEVCTL_PM_BUS)) { + errno = EINVAL; + return (-1); + } + + rv = dc_cmd(DEVCTL_PM_CHANGE_PWR_LOW, 0, DCP(dcp), NULL, NULL); + + if (_libdevice_debug) + (void) printf("devctl_pm_changepowerlow: %d\n", rv); + + return (rv); +} + +/* + * mark component 0 idle + */ +int +devctl_pm_idlecomponent(devctl_hdl_t dcp) +{ + int rv; + + if (dcp == NULL || (DCP(dcp)->hdltype != DEVCTL_PM_DEV && + DCP(dcp)->hdltype != DEVCTL_PM_BUS)) { + errno = EINVAL; + return (-1); + } + + rv = dc_cmd(DEVCTL_PM_IDLE_COMP, 0, DCP(dcp), NULL, NULL); + + if (_libdevice_debug) + (void) printf("devctl_pm_idlecomponent: %d\n", rv); + + return (rv); +} + +/* + * mark component 0 busy + */ +int +devctl_pm_busycomponent(devctl_hdl_t dcp) +{ + int rv; + + if (dcp == NULL || (DCP(dcp)->hdltype != DEVCTL_PM_DEV && + DCP(dcp)->hdltype != DEVCTL_PM_BUS)) { + errno = EINVAL; + return (-1); + } + + rv = dc_cmd(DEVCTL_PM_BUSY_COMP, 0, DCP(dcp), NULL, NULL); + + if (_libdevice_debug) + (void) printf("devctl_pm_busycomponent: %d\n", rv); + + return (rv); +} + +/* + * test pm busy state + */ +int +devctl_pm_testbusy(devctl_hdl_t dcp, uint_t *busystate) +{ + int rv; + uint_t busy_state = 0; + + if (busystate == NULL) { + errno = EINVAL; + return (-1); + } + + if (dcp == NULL || (DCP(dcp)->hdltype != DEVCTL_PM_DEV && + DCP(dcp)->hdltype != DEVCTL_PM_BUS)) { + errno = EINVAL; + return (-1); + } + + rv = dc_cmd(DEVCTL_PM_BUSY_COMP_TEST, 0, DCP(dcp), NULL, + (void *)&busy_state); + + if (rv == -1) + *busystate = 0; + else + *busystate = busy_state; + + if (_libdevice_debug) + (void) printf("devctl_pm_bus_testbusy: rv %d busystate %x\n", + rv, *busystate); + + return (rv); +} + +/* + * set flag to fail DDI_SUSPEND + */ +int +devctl_pm_failsuspend(devctl_hdl_t dcp) +{ + int rv; + + if (dcp == NULL || (DCP(dcp)->hdltype != DEVCTL_PM_DEV && + DCP(dcp)->hdltype != DEVCTL_PM_BUS)) { + errno = EINVAL; + return (-1); + } + + rv = dc_cmd(DEVCTL_PM_FAIL_SUSPEND, 0, DCP(dcp), NULL, NULL); + + if (_libdevice_debug) + (void) printf("devctl_pm_failsuspend: %d\n", rv); + return (rv); +} + +int +devctl_pm_bus_teststrict(devctl_hdl_t dcp, uint_t *strict) +{ + int rv; + uint_t strict_state; + + if (strict == NULL) { + errno = EINVAL; + return (-1); + } + + if (dcp == NULL || (DCP(dcp)->hdltype != DEVCTL_PM_BUS)) { + errno = EINVAL; + return (-1); + } + + rv = dc_cmd(DEVCTL_PM_BUS_STRICT_TEST, 0, DCP(dcp), NULL, + (void *)&strict_state); + + if (rv == -1) + *strict = 0; + else + *strict = strict_state; + + if (_libdevice_debug) + (void) printf("devctl_pm_bus_teststrict: rv %d strict %x\n", + rv, *strict); + + return (rv); +} + +/* + * issue prom_printf() call + */ +int +devctl_pm_device_promprintf(devctl_hdl_t dcp) +{ + int rv; + + if (dcp == NULL || (DCP(dcp)->hdltype != DEVCTL_PM_DEV && + DCP(dcp)->hdltype != DEVCTL_PM_BUS)) { + errno = EINVAL; + return (-1); + } + + rv = dc_cmd(DEVCTL_PM_PROM_PRINTF, 0, DCP(dcp), NULL, NULL); + + if (_libdevice_debug) + (void) printf("devctl_pm_device_promprintf: %d\n", rv); + return (rv); +} + +/* + * set flag to power up the device via + * pm_power_has_changed() calls vs. + * pm_raise_power(), during DDI_RESUME + */ +int +devctl_pm_device_changeonresume(devctl_hdl_t dcp) +{ + int rv; + + if (dcp == NULL || (DCP(dcp)->hdltype != DEVCTL_PM_DEV && + DCP(dcp)->hdltype != DEVCTL_PM_BUS)) { + errno = EINVAL; + return (-1); + } + + rv = dc_cmd(DEVCTL_PM_PWR_HAS_CHANGED_ON_RESUME, 0, + DCP(dcp), NULL, NULL); + + if (_libdevice_debug) + (void) printf("devctl_pm_device_changeonresume: %d\n", rv); + return (rv); +} + +/* + * issue DEVCTL_PM_NO_LOWER_POWER to clear the LOWER_POWER_FLAG + * flag: pm_lower_power() will not be called on device detach + */ +int +devctl_pm_device_no_lower_power(devctl_hdl_t dcp) +{ + int rv; + + if (dcp == NULL || DCP(dcp)->hdltype != DEVCTL_PM_DEV) { + errno = EINVAL; + return (-1); + } + + rv = dc_cmd(DEVCTL_PM_NO_LOWER_POWER, 0, DCP(dcp), NULL, NULL); + + if (_libdevice_debug) + (void) printf("devctl_pm_device_no_lower_power: %d\n", rv); + return (rv); +} + +/* + * issue DEVCTL_PM_BUS_NO_INVOL ioctl to set the NO_INVOL_FLAG + * flag: parent driver will mark itself idle twice in + * DDI_CTLOPS_DETACH(POST) + */ +int +devctl_pm_bus_no_invol(devctl_hdl_t dcp) +{ + int rv; + + if (dcp == NULL || DCP(dcp)->hdltype != DEVCTL_PM_BUS) { + errno = EINVAL; + return (-1); + } + + rv = dc_cmd(DEVCTL_PM_BUS_NO_INVOL, 0, DCP(dcp), NULL, NULL); + + if (_libdevice_debug) + (void) printf("devctl_pm_bus_no_invol: %d\n", rv); + return (rv); +} + +/* + * Place the device ONLINE + */ +int +devctl_device_online(devctl_hdl_t dcp) +{ + int rv; + + if (dcp == NULL || DCP(dcp)->hdltype != DEVCTL_DEVICE) { + errno = EINVAL; + return (-1); + } + + rv = dc_cmd(DEVCTL_DEVICE_ONLINE, 0, DCP(dcp), NULL, NULL); + + if (_libdevice_debug) + (void) printf("devctl_device_online: %d\n", rv); + + return (rv); +} + +/* + * take device OFFLINE + */ +int +devctl_device_offline(devctl_hdl_t dcp) +{ + int rv; + + if (dcp == NULL || DCP(dcp)->hdltype != DEVCTL_DEVICE) { + errno = EINVAL; + return (-1); + } + + rv = dc_cmd(DEVCTL_DEVICE_OFFLINE, 0, DCP(dcp), NULL, NULL); + + if (_libdevice_debug) + (void) printf("devctl_device_offline: %d\n", rv); + + return (rv); +} + +/* + * take the device OFFLINE and remove its dev_info node + */ +int +devctl_device_remove(devctl_hdl_t dcp) +{ + int rv; + + if (dcp == NULL || DCP(dcp)->hdltype != DEVCTL_DEVICE) { + errno = EINVAL; + return (-1); + } + + rv = dc_cmd(DEVCTL_DEVICE_REMOVE, 0, DCP(dcp), NULL, NULL); + + if (_libdevice_debug) + (void) printf("devctl_device_remove: %d\n", rv); + + return (rv); +} + + +/* + * QUIESCE the bus + */ +int +devctl_bus_quiesce(devctl_hdl_t dcp) +{ + int rv; + + rv = dc_cmd(DEVCTL_BUS_QUIESCE, 0, DCP(dcp), NULL, NULL); + + if (_libdevice_debug) + (void) printf("devctl_bus_quiesce: %d\n", rv); + + return (rv); +} + +int +devctl_bus_unquiesce(devctl_hdl_t dcp) +{ + int rv; + + rv = dc_cmd(DEVCTL_BUS_UNQUIESCE, 0, DCP(dcp), NULL, NULL); + + if (_libdevice_debug) + (void) printf("devctl_bus_unquiesce: %d\n", rv); + + return (rv); +} + +int +devctl_bus_reset(devctl_hdl_t dcp) +{ + int rv; + + rv = dc_cmd(DEVCTL_BUS_RESET, 0, DCP(dcp), NULL, NULL); + + if (_libdevice_debug) + (void) printf("devctl_bus_reset: %d\n", rv); + + return (rv); +} + +int +devctl_bus_resetall(devctl_hdl_t dcp) +{ + int rv; + + rv = dc_cmd(DEVCTL_BUS_RESETALL, 0, DCP(dcp), NULL, NULL); + + if (_libdevice_debug) + (void) printf("devctl_bus_resetall: %d\n", rv); + + return (rv); +} + +int +devctl_device_reset(devctl_hdl_t dcp) +{ + int rv; + + rv = dc_cmd(DEVCTL_DEVICE_RESET, 0, DCP(dcp), NULL, NULL); + + if (_libdevice_debug) + (void) printf("devctl_device_reset: %d\n", rv); + + return (rv); +} + +int +devctl_device_getstate(devctl_hdl_t dcp, uint_t *devstate) +{ + int rv; + uint_t device_state; + + if (devstate == NULL) { + errno = EINVAL; + return (-1); + } + + rv = dc_cmd(DEVCTL_DEVICE_GETSTATE, 0, DCP(dcp), NULL, + (void *)&device_state); + + if (rv == -1) + *devstate = 0; + else + *devstate = device_state; + + if (_libdevice_debug) + (void) printf("devctl_device_getstate: rv %d state %x\n", + rv, *devstate); + + return (rv); +} + +int +devctl_bus_getstate(devctl_hdl_t dcp, uint_t *devstate) +{ + int rv; + uint_t device_state; + + if (devstate == NULL) { + errno = EINVAL; + return (-1); + } + + rv = dc_cmd(DEVCTL_BUS_GETSTATE, 0, DCP(dcp), NULL, + (void *)&device_state); + + if (rv == -1) + *devstate = 0; + else + *devstate = device_state; + + if (_libdevice_debug) + (void) printf("devctl_bus_getstate: rv %d, state %x\n", + rv, *devstate); + + return (rv); +} + +int +devctl_bus_configure(devctl_hdl_t dcp) +{ + int rv; + + rv = dc_cmd(DEVCTL_BUS_CONFIGURE, 0, DCP(dcp), NULL, NULL); + + if (_libdevice_debug) + (void) printf("devctl_bus_configure: %d\n", rv); + + return (rv); +} + +int +devctl_bus_unconfigure(devctl_hdl_t dcp) +{ + int rv; + + rv = dc_cmd(DEVCTL_BUS_UNCONFIGURE, 0, DCP(dcp), NULL, NULL); + + if (_libdevice_debug) + (void) printf("devctl_bus_unconfigure: %d\n", rv); + + return (rv); +} + +/* + * devctl_bus_dev_create() - create a new child device + * Attempt to construct and attach a new child device below a + * bus nexus (dcp). The device is defined using the devctl_ddef_*() + * routines to specify the set of bus-specific properties required + * to initalize and attach the device. + */ +int +devctl_bus_dev_create(devctl_hdl_t dcp, devctl_ddef_t ddef_hdl, + uint_t flags, devctl_hdl_t *new_dcp) +{ + char devname[MAXNAMELEN]; + char devpath[MAXPATHLEN]; + int rv = 0; + + if (dcp == NULL || ddef_hdl == NULL) { + errno = EINVAL; + return (-1); + } + + (void) memset(devname, 0, sizeof (devname)); + rv = dc_cmd(DEVCTL_BUS_DEV_CREATE, flags, DCP(dcp), + (nvlist_t *)ddef_hdl, devname); + + /* + * construct a device handle for the new device + */ + if ((rv == 0) && (new_dcp != NULL)) { + char *minorname, *lastslash; + + (void) memset(devpath, 0, sizeof (devpath)); + (void) strcat(devpath, DCP(dcp)->opath); + + /* + * Take the pathname of the parent device, chop off + * any minor name info, and append the name@addr of + * the new child device. + * Call dc_mkhndl() with this constructed path and + * the CLONE handle type to create a new handle which + * references the new child device. + */ + lastslash = strrchr(devpath, '/'); + if (*(lastslash + 1) == '\0') { + *lastslash = '\0'; + } else { + if ((minorname = strchr(lastslash, ':')) != NULL) + *minorname = '\0'; + } + (void) strcat(devpath, "/"); + (void) strlcat(devpath, devname, MAXPATHLEN); + *new_dcp = dc_mkhndl(DEVCTL_CLONE, devpath, 0, dcp); + if (*new_dcp == NULL) + rv = -1; + } + + return (rv); +} + +int +devctl_ap_connect(devctl_hdl_t dcp, nvlist_t *ap_data) +{ + int rv; + + rv = dc_cmd(DEVCTL_AP_CONNECT, 0, DCP(dcp), ap_data, NULL); + + if (_libdevice_debug) + (void) printf("devctl_ap_connect: %d\n", rv); + + return (rv); +} + +int +devctl_ap_disconnect(devctl_hdl_t dcp, nvlist_t *ap_data) +{ + int rv; + + rv = dc_cmd(DEVCTL_AP_DISCONNECT, 0, DCP(dcp), ap_data, NULL); + + if (_libdevice_debug) + (void) printf("devctl_ap_disconnect: %d\n", rv); + + return (rv); +} + +int +devctl_ap_insert(devctl_hdl_t dcp, nvlist_t *ap_data) +{ + int rv; + + rv = dc_cmd(DEVCTL_AP_INSERT, 0, DCP(dcp), ap_data, NULL); + + if (_libdevice_debug) + (void) printf("devctl_ap_insert: %d\n", rv); + + return (rv); +} + +int +devctl_ap_remove(devctl_hdl_t dcp, nvlist_t *ap_data) +{ + int rv; + + rv = dc_cmd(DEVCTL_AP_REMOVE, 0, DCP(dcp), ap_data, NULL); + + if (_libdevice_debug) + (void) printf("devctl_ap_remove: %d\n", rv); + + return (rv); +} + +int +devctl_ap_configure(devctl_hdl_t dcp, nvlist_t *ap_data) +{ + int rv; + + rv = dc_cmd(DEVCTL_AP_CONFIGURE, 0, DCP(dcp), ap_data, NULL); + + if (_libdevice_debug) + (void) printf("devctl_ap_configure: %d\n", rv); + + return (rv); +} + +int +devctl_ap_unconfigure(devctl_hdl_t dcp, nvlist_t *ap_data) +{ + int rv; + + rv = dc_cmd(DEVCTL_AP_UNCONFIGURE, 0, DCP(dcp), ap_data, NULL); + + if (_libdevice_debug) + (void) printf("devctl_ap_unconfigure: %d\n", rv); + + return (rv); +} + +int +devctl_ap_getstate(devctl_hdl_t dcp, nvlist_t *ap_data, + devctl_ap_state_t *apstate) +{ + int rv; + devctl_ap_state_t ap_state; + + rv = dc_cmd(DEVCTL_AP_GETSTATE, 0, DCP(dcp), ap_data, + (void *)&ap_state); + + if (rv == -1) + (void) memset(apstate, 0, sizeof (struct devctl_ap_state)); + else + *apstate = ap_state; + + if (_libdevice_debug) + (void) printf("devctl_ap_getstate: %d\n", rv); + + return (rv); +} + +/* + * Allocate a device 'definition' handle, in reality a list of + * nvpair data. + */ +/* ARGSUSED */ +devctl_ddef_t +devctl_ddef_alloc(char *nodename, int flags) +{ + + nvlist_t *nvlp; + + if ((nodename == NULL) || *nodename == '\0') { + errno = EINVAL; + return (NULL); + } + + /* + * allocate nvlist structure which is returned as an + * opaque handle to the caller. If this fails, return + * NULL with errno left set to the value + */ + if (nvlist_alloc(&nvlp, NV_UNIQUE_NAME_TYPE, 0) != 0) { + errno = ENOMEM; + return (NULL); + } + + /* + * add the nodename of the new device to the list + */ + if (nvlist_add_string(nvlp, DC_DEVI_NODENAME, nodename) != 0) { + nvlist_free(nvlp); + errno = ENOMEM; + return (NULL); + } + + if (_libdevice_debug) + (void) printf("devctl_ddef_alloc: node %s nvp %p\n", nodename, + (void *)nvlp); + + return ((devctl_ddef_t)nvlp); +} + +/* + * free the definition handle + */ +void +devctl_ddef_free(devctl_ddef_t ddef_hdl) +{ + if (_libdevice_debug) + (void) printf("devctl_ddef_free: nvp %p\n", (void *)ddef_hdl); + + if (ddef_hdl != NULL) { + nvlist_free((nvlist_t *)ddef_hdl); + } +} + +/* + * define an integer property + */ +int +devctl_ddef_int(devctl_ddef_t ddef_hdl, char *name, int32_t value) +{ + + int rv; + + if (ddef_hdl == NULL || name == NULL || *name == '\0') { + errno = EINVAL; + return (-1); + } + + rv = nvlist_add_int32((nvlist_t *)ddef_hdl, name, value); + + if (_libdevice_debug) + (void) printf("devctl_ddef_int: rv %d nvp %p name %s val %d\n", + rv, (void *)ddef_hdl, name, value); + + return (rv); +} + +/* + * define an integer array property + */ +int +devctl_ddef_int_array(devctl_ddef_t ddef_hdl, char *name, int nelements, + int32_t *value) +{ + int rv, i; + + if (ddef_hdl == NULL || name == NULL || *name == '\0') { + errno = EINVAL; + return (-1); + } + + rv = nvlist_add_int32_array((nvlist_t *)ddef_hdl, name, value, + nelements); + + if (_libdevice_debug) { + (void) printf("devctl_ddef_int_array: rv %d nvp %p name %s: ", + rv, (void *)ddef_hdl, name); + for (i = 0; i < nelements; i++) + (void) printf("0x%x ", value[i]); + (void) printf("\n"); + } + + return (rv); +} + +/* + * define a string property + */ +int +devctl_ddef_string(devctl_ddef_t ddef_hdl, char *name, char *value) +{ + int rv; + + if (ddef_hdl == NULL || name == NULL || *name == '\0') { + errno = EINVAL; + return (-1); + } + + rv = nvlist_add_string((nvlist_t *)ddef_hdl, name, value); + + if (_libdevice_debug) + (void) printf("devctl_ddef_string: rv %d nvp %p %s=\"%s\"\n", + rv, (void *)ddef_hdl, name, value); + + return (rv); +} + +/* + * define a string array property + */ +int +devctl_ddef_string_array(devctl_ddef_t ddef_hdl, char *name, int nelements, + char **value) +{ + int rv, i; + + if (ddef_hdl == NULL || name == NULL || *name == '\0') { + errno = EINVAL; + return (-1); + } + + rv = nvlist_add_string_array((nvlist_t *)ddef_hdl, name, + value, nelements); + + if (_libdevice_debug) { + (void) printf("devctl_ddef_string_array: rv %d nvp %p " + "name %s:\n", rv, (void *)ddef_hdl, name); + for (i = 0; i < nelements; i++) + (void) printf("\t%d: \"%s\"\n", i, value[i]); + } + return (rv); +} + +/* + * define a byte array property + */ +int +devctl_ddef_byte_array(devctl_ddef_t ddef_hdl, char *name, int nelements, + uchar_t *value) +{ + int rv; + + if (ddef_hdl == NULL || name == NULL || *name == '\0') { + errno = EINVAL; + return (-1); + } + + rv = nvlist_add_byte_array((nvlist_t *)ddef_hdl, name, value, + nelements); + + return (rv); +} + +/* + * return the pathname which was used to acquire the handle + */ +char * +devctl_get_pathname(devctl_hdl_t dcp, char *pathbuf, size_t bufsz) +{ + if (dcp == NULL || pathbuf == NULL || bufsz == 0) { + errno = EINVAL; + return (NULL); + } + + (void) snprintf(pathbuf, bufsz, "%s", DCP(dcp)->opath); + return (pathbuf); +} + + +/* + * execute the IOCTL request + */ +static int +dc_cmd(uint_t cmd, uint_t flags, struct devctl_hdl *dcp, nvlist_t *ulp, + void *retinfo) +{ + struct devctl_iocdata iocdata; + int rv = 0; + + if (_libdevice_debug) + (void) printf("dc_cmd: %x dcp %p ulp %p flags %x rv %p\n", cmd, + (void *)dcp, (void *)ulp, flags, retinfo); + + if ((dcp == NULL) || (DCP(dcp)->fd == -1)) { + errno = EINVAL; + return (-1); + } + + (void) memset(&iocdata, 0, sizeof (struct devctl_iocdata)); + + /* + * if there was any user supplied data in the form of a nvlist, + * pack the list prior to copyin. + */ + if (ulp != NULL) { + if (rv = nvlist_pack(ulp, (char **)&iocdata.nvl_user, + &iocdata.nvl_usersz, NV_ENCODE_NATIVE, 0)) { + /* + * exit with errno set by nvlist_pack() + */ + goto exit; + } + } else { + iocdata.nvl_user = NULL; + iocdata.nvl_usersz = 0; + } + + /* + * finish initalizing the request and execute the IOCTL + */ + iocdata.cmd = cmd; + iocdata.flags = flags; + iocdata.c_nodename = dcp->nodename; + iocdata.c_unitaddr = dcp->unitaddr; + iocdata.cpyout_buf = retinfo; + rv = ioctl(dcp->fd, cmd, &iocdata); + if (rv < 0 && _libdevice_debug) { + (void) printf("dc_cmd: exited with rv %d, errno(%d):%s\n", + rv, errno, strerror(errno)); + } + +exit: + if (iocdata.nvl_user != NULL) + free(iocdata.nvl_user); + + return (rv); +} diff --git a/usr/src/lib/libdevice/libdevice.h b/usr/src/lib/libdevice/libdevice.h new file mode 100644 index 0000000..22a8ab1 --- /dev/null +++ b/usr/src/lib/libdevice/libdevice.h @@ -0,0 +1,189 @@ +/* + * 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. + */ + +#ifndef _LIBDEVICE_H +#define _LIBDEVICE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/sunddi.h> +#include <sys/ddi_impldefs.h> +#include <sys/devctl.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define DC_EXCL 0x01 +#define DC_RDONLY 0x02 + +typedef struct devctl_dummy_struct *devctl_hdl_t; +typedef struct devctl_dummy_ddef *devctl_ddef_t; + + +devctl_hdl_t +devctl_device_acquire(char *devfs_path, uint_t flags); + +devctl_hdl_t +devctl_bus_acquire(char *devfs_path, uint_t flags); + +devctl_hdl_t +devctl_ap_acquire(char *devfs_path, uint_t flags); + +devctl_hdl_t +devctl_pm_dev_acquire(char *devfs_path, uint_t flags); + +devctl_hdl_t +devctl_pm_bus_acquire(char *devfs_path, uint_t flags); + +void +devctl_release(devctl_hdl_t hdl); + +int +devctl_device_offline(devctl_hdl_t hdl); + +int +devctl_device_remove(devctl_hdl_t hdl); + +int +devctl_pm_raisepower(devctl_hdl_t hdl); + +int +devctl_pm_changepowerlow(devctl_hdl_t hdl); + +int +devctl_pm_changepowerhigh(devctl_hdl_t hdl); + +int +devctl_pm_idlecomponent(devctl_hdl_t hdl); + +int +devctl_pm_busycomponent(devctl_hdl_t hdl); + +int +devctl_pm_testbusy(devctl_hdl_t hdl, uint_t *busyp); + +int +devctl_pm_failsuspend(devctl_hdl_t hdl); + +int +devctl_pm_bus_teststrict(devctl_hdl_t hdl, uint_t *strict); + +int +devctl_pm_device_changeonresume(devctl_hdl_t hdl); + +int +devctl_pm_device_no_lower_power(devctl_hdl_t hdl); + +int +devctl_pm_bus_no_invol(devctl_hdl_t hdl); + +int +devctl_pm_device_promprintf(devctl_hdl_t hdl); + +int +devctl_device_online(devctl_hdl_t hdl); + +int +devctl_device_reset(devctl_hdl_t hdl); + +int +devctl_device_getstate(devctl_hdl_t hdl, uint_t *statep); + +int +devctl_bus_quiesce(devctl_hdl_t hdl); + +int +devctl_bus_unquiesce(devctl_hdl_t hdl); + +int +devctl_bus_reset(devctl_hdl_t hdl); + +int +devctl_bus_resetall(devctl_hdl_t hdl); + +int +devctl_bus_getstate(devctl_hdl_t hdl, uint_t *statep); + +int +devctl_bus_configure(devctl_hdl_t hdl); + +int +devctl_bus_unconfigure(devctl_hdl_t hdl); + +int +devctl_ap_insert(devctl_hdl_t, nvlist_t *); + +int +devctl_ap_remove(devctl_hdl_t, nvlist_t *); + +int +devctl_ap_connect(devctl_hdl_t, nvlist_t *); + +int +devctl_ap_disconnect(devctl_hdl_t, nvlist_t *); + +int +devctl_ap_configure(devctl_hdl_t, nvlist_t *); + +int +devctl_ap_unconfigure(devctl_hdl_t, nvlist_t *); + +int +devctl_ap_getstate(devctl_hdl_t, nvlist_t *, devctl_ap_state_t *); + +devctl_ddef_t +devctl_ddef_alloc(char *, int); + +void +devctl_ddef_free(devctl_ddef_t); + +int +devctl_ddef_int(devctl_ddef_t, char *, int32_t); + +int +devctl_ddef_int_array(devctl_ddef_t, char *, int, int32_t *); + +int +devctl_ddef_string(devctl_ddef_t ddef_hdl, char *, char *); + +int +devctl_ddef_string_array(devctl_ddef_t, char *, int, char **); + +int +devctl_ddef_byte_array(devctl_ddef_t, char *, int, uchar_t *); + +int +devctl_bus_dev_create(devctl_hdl_t, devctl_ddef_t, uint_t, devctl_hdl_t *); + +char * +devctl_get_pathname(devctl_hdl_t, char *, size_t); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBDEVICE_H */ |