/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
*/
#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 components 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);
}