| /* |
| * 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); |
| } |