#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <stropts.h>
#include <fcntl.h>
#include <poll.h>
#include <synch.h>
#include <unistd.h>
#include <sys/mkdev.h>
#include <sys/obpdefs.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/autoconf.h>
#include <stdarg.h>
#include <sys/ddi_hp.h>
#define NDEBUG 1
#include <assert.h>
#include "libdevinfo.h"
typedef enum {
DI_QUIET = 0,
DI_ERR = 1,
DI_INFO,
DI_TRACE,
DI_TRACE1,
DI_TRACE2
} di_debug_t;
int di_debug = DI_QUIET;
#define DPRINTF(args) { if (di_debug != DI_QUIET) dprint args; }
void dprint(di_debug_t msglevel, const char *fmt, ...);
#pragma init(_libdevinfo_init)
void
_libdevinfo_init()
{
char *debug_str = getenv("_LIBDEVINFO_DEBUG");
if (debug_str) {
errno = 0;
di_debug = atoi(debug_str);
if (errno || di_debug < DI_QUIET)
di_debug = DI_QUIET;
}
}
di_node_t
di_init(const char *phys_path, uint_t flag)
{
return (di_init_impl(phys_path, flag, NULL));
}
static int
blocking_open(const char *path, int oflag)
{
int fd;
while ((fd = open(path, oflag)) == -1 && errno == EAGAIN)
(void) poll(NULL, 0, 1 * MILLISEC);
return (fd);
}
di_node_t
di_init_driver(const char *drv_name, uint_t flag)
{
int fd;
char driver[MAXPATHLEN];
if ((drv_name == NULL) || (strlen(drv_name) >= MAXPATHLEN)) {
errno = EINVAL;
return (DI_NODE_NIL);
}
(void) strcpy(driver, drv_name);
if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo",
O_RDONLY)) == -1) {
DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n", errno));
return (DI_NODE_NIL);
}
if (ioctl(fd, DINFOLODRV, driver) != 0) {
DPRINTF((DI_ERR, "failed to load driver %s\n", driver));
(void) close(fd);
errno = ENXIO;
return (DI_NODE_NIL);
}
(void) close(fd);
return (di_init("/", flag));
}
di_node_t
di_init_impl(const char *phys_path, uint_t flag,
struct di_priv_data *priv)
{
caddr_t pa;
int fd, map_size;
struct di_all *dap;
struct dinfo_io dinfo_io;
uint_t pageoffset = sysconf(_SC_PAGESIZE) - 1;
uint_t pagemask = ~pageoffset;
DPRINTF((DI_INFO, "di_init: taking a snapshot\n"));
if (phys_path == NULL ||
strchr(phys_path, ':') ||
(strncmp(phys_path, "/devices", 8) == 0) ||
(strlen(phys_path) > MAXPATHLEN)) {
errno = EINVAL;
return (DI_NODE_NIL);
}
if (strlen(phys_path) == 0)
(void) sprintf(dinfo_io.root_path, "/");
else if (*phys_path != '/')
(void) snprintf(dinfo_io.root_path, sizeof (dinfo_io.root_path),
"/%s", phys_path);
else
(void) snprintf(dinfo_io.root_path, sizeof (dinfo_io.root_path),
"%s", phys_path);
if (flag & DINFOPRIVDATA & 0xff) {
if (priv)
bcopy(priv, &dinfo_io.priv,
sizeof (struct di_priv_data));
else {
errno = EINVAL;
return (DI_NODE_NIL);
}
}
if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo",
O_RDONLY)) == -1) {
if ((flag & DINFOFORCE) == DINFOFORCE ||
(flag & DINFOPRIVDATA) == DINFOPRIVDATA) {
DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n",
errno));
return (DI_NODE_NIL);
}
if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo,ro",
O_RDONLY)) == -1) {
DPRINTF((DI_ERR, "devinfo open failed: errno = %d\n",
errno));
return (DI_NODE_NIL);
}
}
if (ioctl(fd, DINFOIDENT, NULL) != DI_MAGIC) {
DPRINTF((DI_ERR,
"driver ID failed; check for major conflict\n"));
(void) close(fd);
return (DI_NODE_NIL);
}
if ((map_size = ioctl(fd, flag, &dinfo_io)) < 0) {
DPRINTF((DI_ERR, "devinfo ioctl failed with "
"error: %d\n", errno));
(void) close(fd);
return (DI_NODE_NIL);
} else if (map_size == 0) {
DPRINTF((DI_ERR, "%s not found\n", phys_path));
errno = ENXIO;
(void) close(fd);
return (DI_NODE_NIL);
}
map_size = (map_size + pageoffset) & pagemask;
if ((pa = valloc(map_size)) == NULL) {
DPRINTF((DI_ERR, "valloc failed for snapshot\n"));
(void) close(fd);
return (DI_NODE_NIL);
}
if (ioctl(fd, DINFOUSRLD, pa) != map_size) {
DPRINTF((DI_ERR, "failed to copy snapshot to usrld\n"));
(void) close(fd);
free(pa);
errno = EFAULT;
return (DI_NODE_NIL);
}
(void) close(fd);
dap = DI_ALL(pa);
if (dap->version != DI_SNAPSHOT_VERSION) {
DPRINTF((DI_ERR, "wrong snapshot version "
"(expected=%d, actual=%d)\n",
DI_SNAPSHOT_VERSION, dap->version));
free(pa);
errno = ESTALE;
return (DI_NODE_NIL);
}
if (dap->top_devinfo == 0) {
DPRINTF((DI_ERR, "%s not found\n", phys_path));
free(pa);
errno = EINVAL;
return (DI_NODE_NIL);
}
return (DI_NODE(pa + dap->top_devinfo));
}
void
di_fini(di_node_t root)
{
caddr_t pa;
DPRINTF((DI_INFO, "di_fini: freeing a snapshot\n"));
if (root == DI_NODE_NIL) {
DPRINTF((DI_ERR, "di_fini called with NIL arg\n"));
return;
}
pa = (caddr_t)root - DI_NODE(root)->self;
free(pa);
}
di_node_t
di_parent_node(di_node_t node)
{
caddr_t pa;
if (node == DI_NODE_NIL) {
errno = EINVAL;
return (DI_NODE_NIL);
}
DPRINTF((DI_TRACE, "Get parent of node %s\n", di_node_name(node)));
pa = (caddr_t)node - DI_NODE(node)->self;
if (DI_NODE(node)->parent) {
return (DI_NODE(pa + DI_NODE(node)->parent));
}
if (strcmp(DI_ALL(pa)->root_path, "/") != 0)
errno = ENOTSUP;
else
errno = ENXIO;
return (DI_NODE_NIL);
}
di_node_t
di_sibling_node(di_node_t node)
{
caddr_t pa;
if (node == DI_NODE_NIL) {
errno = EINVAL;
return (DI_NODE_NIL);
}
DPRINTF((DI_TRACE, "Get sibling of node %s\n", di_node_name(node)));
pa = (caddr_t)node - DI_NODE(node)->self;
if (DI_NODE(node)->sibling) {
return (DI_NODE(pa + DI_NODE(node)->sibling));
}
if (!(DI_ALL(pa)->command & DINFOSUBTREE))
errno = ENOTSUP;
else
errno = ENXIO;
return (DI_NODE_NIL);
}
di_node_t
di_child_node(di_node_t node)
{
caddr_t pa;
DPRINTF((DI_TRACE, "Get child of node %s\n", di_node_name(node)));
if (node == DI_NODE_NIL) {
errno = EINVAL;
return (DI_NODE_NIL);
}
pa = (caddr_t)node - DI_NODE(node)->self;
if (DI_NODE(node)->child) {
return (DI_NODE(pa + DI_NODE(node)->child));
}
if (!(DI_ALL(pa)->command & DINFOSUBTREE))
errno = ENOTSUP;
else
errno = ENXIO;
return (DI_NODE_NIL);
}
di_node_t
di_drv_first_node(const char *drv_name, di_node_t root)
{
caddr_t pa;
int major, devcnt;
struct di_devnm *devnm;
DPRINTF((DI_INFO, "Get first node of driver %s\n", drv_name));
if (root == DI_NODE_NIL) {
errno = EINVAL;
return (DI_NODE_NIL);
}
pa = (caddr_t)root - DI_NODE(root)->self;
devcnt = DI_ALL(pa)->devcnt;
devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames);
for (major = 0; major < devcnt; major++)
if (devnm[major].name && (strcmp(drv_name,
(char *)(pa + devnm[major].name)) == 0))
break;
if (major >= devcnt) {
errno = EINVAL;
return (DI_NODE_NIL);
}
if (!(devnm[major].head)) {
errno = ENXIO;
return (DI_NODE_NIL);
}
return (DI_NODE(pa + devnm[major].head));
}
di_node_t
di_drv_next_node(di_node_t node)
{
caddr_t pa;
if (node == DI_NODE_NIL) {
errno = EINVAL;
return (DI_NODE_NIL);
}
DPRINTF((DI_TRACE, "next node on per driver list:"
" current=%s, driver=%s\n",
di_node_name(node), di_driver_name(node)));
if (DI_NODE(node)->next == (di_off_t)-1) {
errno = ENOTSUP;
return (DI_NODE_NIL);
}
pa = (caddr_t)node - DI_NODE(node)->self;
if (DI_NODE(node)->next == 0) {
errno = ENXIO;
return (DI_NODE_NIL);
}
return (DI_NODE(pa + DI_NODE(node)->next));
}
struct node_list {
struct node_list *next;
di_node_t node;
};
static void
free_node_list(struct node_list **headp)
{
struct node_list *tmp;
while (*headp) {
tmp = *headp;
*headp = (*headp)->next;
free(tmp);
}
}
static void
append_node_list(struct node_list **headp, struct node_list *list)
{
struct node_list *tmp;
if (*headp == NULL) {
*headp = list;
return;
}
if (list == NULL)
return;
tmp = *headp;
while (tmp->next)
tmp = tmp->next;
tmp->next = list;
}
static void
prepend_node_list(struct node_list **headp, struct node_list *list)
{
struct node_list *tmp;
if (list == NULL)
return;
tmp = *headp;
*headp = list;
if (tmp == NULL)
return;
while (list->next)
list = list->next;
list->next = tmp;
}
static int
is_descendant(di_node_t node, di_node_t parent)
{
if (parent == DI_NODE_NIL) {
return (1);
}
do {
node = di_parent_node(node);
} while ((node != DI_NODE_NIL) && (node != parent));
return (node != DI_NODE_NIL);
}
static void
insert_node_list(struct node_list **headp, struct node_list *list,
di_node_t parent)
{
struct node_list *tmp, *tmp1;
if (list == NULL)
return;
tmp = *headp;
if (tmp == NULL) {
*headp = list;
return;
}
if (!is_descendant(tmp->node, parent)) {
prepend_node_list(headp, list);
return;
}
while (tmp->next && is_descendant(tmp->next->node, parent)) {
tmp = tmp->next;
}
tmp1 = tmp->next;
tmp->next = list;
append_node_list(headp, tmp1);
}
static struct node_list *
get_children(di_node_t node)
{
di_node_t child;
struct node_list *result, *tmp;
DPRINTF((DI_TRACE1, "Get children of node %s\n", di_node_name(node)));
if ((child = di_child_node(node)) == DI_NODE_NIL) {
return (NULL);
}
if ((result = malloc(sizeof (struct node_list))) == NULL) {
DPRINTF((DI_ERR, "malloc of node_list failed\n"));
return (NULL);
}
result->node = child;
tmp = result;
while ((child = di_sibling_node(tmp->node)) != DI_NODE_NIL) {
if ((tmp->next = malloc(sizeof (struct node_list))) == NULL) {
DPRINTF((DI_ERR, "malloc of node_list failed\n"));
free_node_list(&result);
return (NULL);
}
tmp = tmp->next;
tmp->node = child;
}
tmp->next = NULL;
return (result);
}
static void
prune_sib(struct node_list **headp)
{
di_node_t parent, curr_par, curr_gpar;
struct node_list *curr, *prev;
if ((parent = di_parent_node((*headp)->node)) == DI_NODE_NIL) {
if ((*headp)->next)
DPRINTF((DI_ERR, "Unexpected err in di_walk_node.\n"));
free(*headp);
*headp = NULL;
return;
}
prev = *headp;
curr = prev->next;
while (curr) {
if (((curr_par = di_parent_node(curr->node)) != DI_NODE_NIL) &&
((curr_par == parent) || ((curr_gpar =
di_parent_node(curr_par)) != DI_NODE_NIL) &&
(curr_gpar == parent))) {
prev->next = curr->next;
free(curr);
curr = prev->next;
} else
curr = curr->next;
}
curr = *headp;
*headp = curr->next;
free(curr);
}
static void
update_node_list(int action, uint_t flag, struct node_list **headp)
{
struct node_list *children, *tmp;
di_node_t parent = di_parent_node((*headp)->node);
switch (action) {
case DI_WALK_TERMINATE:
children = NULL;
free_node_list(headp);
break;
case DI_WALK_PRUNESIB:
children = get_children((*headp)->node);
prune_sib(headp);
break;
case DI_WALK_PRUNECHILD:
children = NULL;
tmp = *headp;
*headp = tmp->next;
free(tmp);
break;
case DI_WALK_CONTINUE:
default:
children = get_children((*headp)->node);
tmp = *headp;
*headp = tmp->next;
free(tmp);
break;
}
switch (flag) {
case DI_WALK_CLDFIRST:
prepend_node_list(headp, children);
break;
case DI_WALK_SIBFIRST:
append_node_list(headp, children);
break;
case DI_WALK_LINKGEN:
default:
insert_node_list(headp, children, parent);
break;
}
}
static void
walk_one_node(struct node_list **headp, uint_t flag, void *arg,
int (*callback)(di_node_t, void *))
{
DPRINTF((DI_TRACE, "Walking node %s\n", di_node_name((*headp)->node)));
update_node_list(callback((*headp)->node, arg),
flag & DI_WALK_MASK, headp);
}
int
di_walk_node(di_node_t root, uint_t flag, void *arg,
int (*node_callback)(di_node_t, void *))
{
struct node_list *head;
if (root == NULL) {
errno = EINVAL;
return (-1);
}
if ((head = malloc(sizeof (struct node_list))) == NULL) {
DPRINTF((DI_ERR, "malloc of node_list failed\n"));
return (-1);
}
head->next = NULL;
head->node = root;
DPRINTF((DI_INFO, "Start node walking from node %s\n",
di_node_name(root)));
while (head != NULL)
walk_one_node(&head, flag, arg, node_callback);
return (0);
}
static void
walk_one_minor_list(struct node_list **headp, const char *desired_type,
uint_t flag, void *arg, int (*callback)(di_node_t, di_minor_t, void *))
{
int ddm_type;
int action = DI_WALK_CONTINUE;
char *node_type;
di_minor_t minor = DI_MINOR_NIL;
di_node_t node = (*headp)->node;
while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
ddm_type = di_minor_type(minor);
if ((ddm_type == DDM_ALIAS) && !(flag & DI_CHECK_ALIAS))
continue;
if ((ddm_type == DDM_INTERNAL_PATH) &&
!(flag & DI_CHECK_INTERNAL_PATH))
continue;
node_type = di_minor_nodetype(minor);
if ((desired_type != NULL) && ((node_type == NULL) ||
strncmp(desired_type, node_type, strlen(desired_type))
!= 0))
continue;
if ((action = callback(node, minor, arg)) ==
DI_WALK_TERMINATE) {
break;
}
}
update_node_list(action, DI_WALK_LINKGEN, headp);
}
int
di_walk_minor(di_node_t root, const char *minor_type, uint_t flag, void *arg,
int (*minor_callback)(di_node_t, di_minor_t, void *))
{
struct node_list *head;
#ifdef DEBUG
char *devfspath = di_devfs_path(root);
DPRINTF((DI_INFO, "walking minor nodes under %s\n", devfspath));
di_devfs_path_free(devfspath);
#endif
if (root == NULL) {
errno = EINVAL;
return (-1);
}
if ((head = malloc(sizeof (struct node_list))) == NULL) {
DPRINTF((DI_ERR, "malloc of node_list failed\n"));
return (-1);
}
head->next = NULL;
head->node = root;
DPRINTF((DI_INFO, "Start minor walking from node %s\n",
di_node_name(root)));
while (head != NULL)
walk_one_minor_list(&head, minor_type, flag, arg,
minor_callback);
return (0);
}
char *
di_node_name(di_node_t node)
{
return ((caddr_t)node + DI_NODE(node)->node_name - DI_NODE(node)->self);
}
char *
di_bus_addr(di_node_t node)
{
caddr_t pa = (caddr_t)node - DI_NODE(node)->self;
if (DI_NODE(node)->address == 0)
return (NULL);
return ((char *)(pa + DI_NODE(node)->address));
}
char *
di_binding_name(di_node_t node)
{
caddr_t pa = (caddr_t)node - DI_NODE(node)->self;
if (DI_NODE(node)->bind_name == 0)
return (NULL);
return ((char *)(pa + DI_NODE(node)->bind_name));
}
int
di_compatible_names(di_node_t node, char **names)
{
char *c;
int len, size, entries = 0;
if (DI_NODE(node)->compat_names == 0) {
*names = NULL;
return (0);
}
*names = (caddr_t)node +
DI_NODE(node)->compat_names - DI_NODE(node)->self;
c = *names;
len = DI_NODE(node)->compat_length;
while (len > 0) {
entries++;
size = strlen(c) + 1;
len -= size;
c += size;
}
return (entries);
}
int
di_instance(di_node_t node)
{
return (DI_NODE(node)->instance);
}
int
di_nodeid(di_node_t node)
{
if (DI_NODE(node)->node_class == DDI_NC_PROM)
return (DI_PROM_NODEID);
if (DI_NODE(node)->attributes & DDI_PERSISTENT)
return (DI_SID_NODEID);
return (DI_PSEUDO_NODEID);
}
uint_t
di_state(di_node_t node)
{
uint_t result = 0;
if (di_node_state(node) < DS_ATTACHED)
result |= DI_DRIVER_DETACHED;
if (DI_NODE(node)->state & DEVI_DEVICE_OFFLINE)
result |= DI_DEVICE_OFFLINE;
if (DI_NODE(node)->state & DEVI_DEVICE_DOWN)
result |= DI_DEVICE_DOWN;
if (DI_NODE(node)->state & DEVI_DEVICE_DEGRADED)
result |= DI_DEVICE_DEGRADED;
if (DI_NODE(node)->state & DEVI_DEVICE_REMOVED)
result |= DI_DEVICE_REMOVED;
if (DI_NODE(node)->state & DEVI_BUS_QUIESCED)
result |= DI_BUS_QUIESCED;
if (DI_NODE(node)->state & DEVI_BUS_DOWN)
result |= DI_BUS_DOWN;
return (result);
}
ddi_node_state_t
di_node_state(di_node_t node)
{
return (DI_NODE(node)->node_state);
}
uint_t
di_flags(di_node_t node)
{
return (DI_NODE(node)->flags);
}
uint_t
di_retired(di_node_t node)
{
return (di_flags(node) & DEVI_RETIRED);
}
ddi_devid_t
di_devid(di_node_t node)
{
if (DI_NODE(node)->devid == 0)
return (NULL);
return ((ddi_devid_t)((caddr_t)node +
DI_NODE(node)->devid - DI_NODE(node)->self));
}
int
di_driver_major(di_node_t node)
{
int major;
major = DI_NODE(node)->drv_major;
if (major < 0)
return (-1);
return (major);
}
char *
di_driver_name(di_node_t node)
{
int major;
caddr_t pa;
struct di_devnm *devnm;
major = DI_NODE(node)->drv_major;
if (major < 0)
return (NULL);
pa = (caddr_t)node - DI_NODE(node)->self;
devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames);
if (devnm[major].name)
return (pa + devnm[major].name);
else
return (NULL);
}
uint_t
di_driver_ops(di_node_t node)
{
int major;
caddr_t pa;
struct di_devnm *devnm;
major = DI_NODE(node)->drv_major;
if (major < 0)
return (0);
pa = (caddr_t)node - DI_NODE(node)->self;
devnm = DI_DEVNM(pa + DI_ALL(pa)->devnames);
return (devnm[major].ops);
}
char *
di_devfs_path(di_node_t node)
{
caddr_t pa;
di_node_t parent;
int depth = 0, len = 0;
char *buf, *name[MAX_TREE_DEPTH], *addr[MAX_TREE_DEPTH];
if (node == DI_NODE_NIL) {
errno = EINVAL;
return (NULL);
}
while ((parent = di_parent_node(node)) != DI_NODE_NIL) {
name[depth] = di_node_name(node);
len += strlen(name[depth]) + 1;
if ((addr[depth] = di_bus_addr(node)) != NULL)
len += strlen(addr[depth]) + 1;
node = parent;
depth++;
}
pa = (caddr_t)node - DI_NODE(node)->self;
name[depth] = DI_ALL(pa)->root_path;
len += strlen(name[depth]) + 1;
if ((buf = malloc(len)) == NULL) {
return (NULL);
}
(void) strcpy(buf, name[depth]);
len = strlen(buf);
if (buf[len - 1] == '/')
len--;
while (depth) {
depth--;
buf[len] = '/';
(void) strcpy(buf + len + 1, name[depth]);
len += strlen(name[depth]) + 1;
if (addr[depth] && addr[depth][0] != '\0') {
buf[len] = '@';
(void) strcpy(buf + len + 1, addr[depth]);
len += strlen(addr[depth]) + 1;
}
}
return (buf);
}
char *
di_devfs_minor_path(di_minor_t minor)
{
di_node_t node;
char *full_path, *name, *devfspath;
int full_path_len;
if (minor == DI_MINOR_NIL) {
errno = EINVAL;
return (NULL);
}
name = di_minor_name(minor);
node = di_minor_devinfo(minor);
devfspath = di_devfs_path(node);
if (devfspath == NULL)
return (NULL);
full_path_len = strlen(devfspath) + strlen(name) + 2;
full_path = (char *)calloc(1, full_path_len);
if (full_path != NULL)
(void) snprintf(full_path, full_path_len, "%s:%s",
devfspath, name);
di_devfs_path_free(devfspath);
return (full_path);
}
char *
di_path_devfs_path(di_path_t path)
{
di_node_t phci_node;
char *phci_path, *path_name, *path_addr;
char *full_path;
int full_path_len;
if (path == DI_PATH_NIL) {
errno = EINVAL;
return (NULL);
}
path_name = di_path_node_name(path);
path_addr = di_path_bus_addr(path);
if ((path_name == NULL) || (path_addr == NULL))
return (NULL);
phci_node = di_path_phci_node(path);
if (phci_node == NULL)
return (NULL);
phci_path = di_devfs_path(phci_node);
if (phci_path == NULL)
return (NULL);
full_path_len = strlen(phci_path) + 1 + strlen(path_name) +
1 + strlen(path_addr) + 1;
full_path = (char *)calloc(1, full_path_len);
if (full_path != NULL)
(void) snprintf(full_path, full_path_len, "%s/%s@%s",
phci_path, path_name, path_addr);
di_devfs_path_free(phci_path);
return (full_path);
}
char *
di_path_client_devfs_path(di_path_t path)
{
return (di_devfs_path(di_path_client_node(path)));
}
void
di_devfs_path_free(char *buf)
{
if (buf == NULL) {
DPRINTF((DI_ERR, "di_devfs_path_free NULL arg!\n"));
return;
}
free(buf);
}
static int
is_generic(const char *name, int len)
{
const char **gp;
static const char *generic_names[] = {
"atm",
"disk",
"display",
"dma-controller",
"ethernet",
"fcs",
"fdc",
"fddi",
"fibre-channel",
"ide",
"interrupt-controller",
"isa",
"keyboard",
"memory",
"mouse",
"nvram",
"pc-card",
"pci",
"printer",
"rtc",
"sbus",
"scanner",
"scsi",
"serial",
"sound",
"ssa",
"tape",
"timer",
"token-ring",
"vme",
0
};
for (gp = generic_names; *gp; gp++) {
if ((strncmp(*gp, name, len) == 0) &&
(strlen(*gp) == len))
return (1);
}
return (0);
}
int
di_devfs_path_match(const char *dp1, const char *dp2)
{
const char *p1, *p2;
const char *ec1, *ec2;
const char *at1, *at2;
char nc;
int g1, g2;
for (p1 = dp1, p2 = dp2; (*p1 == *p2) && *p1; p1++, p2++) {
if (*p1 != '/')
continue;
nc = *(p1 + 1);
if ((nc == '\0') || (nc == '/'))
continue;
p1++;
p2++;
ec1 = strchr(p1, '/');
if (ec1 == NULL)
ec1 = p1 + strlen(p1);
ec2 = strchr(p2, '/');
if (ec2 == NULL)
ec2 = p2 + strlen(p2);
at1 = strchr(p1, '@');
at2 = strchr(p2, '@');
if (at1 && (at1 < ec1))
ec1 = at1;
if (at2 && (at2 < ec2))
ec2 = at2;
g1 = is_generic(p1, ec1 - p1);
g2 = is_generic(p2, ec2 - p2);
if (g1 != g2) {
p1 = ec1;
p2 = ec2;
} else {
if (*p1 != *p2)
break;
}
}
return ((*p1 == *p2) ? 1 : 0);
}
di_minor_t
di_minor_next(di_node_t node, di_minor_t minor)
{
caddr_t pa;
if (node == DI_NODE_NIL) {
errno = EINVAL;
return (DI_MINOR_NIL);
}
if (minor != DI_MINOR_NIL) {
if (DI_MINOR(minor)->next != 0)
return ((di_minor_t)((void *)((caddr_t)minor -
DI_MINOR(minor)->self + DI_MINOR(minor)->next)));
else {
errno = ENXIO;
return (DI_MINOR_NIL);
}
}
if (DI_NODE(node)->minor_data != 0) {
return (DI_MINOR((caddr_t)node - DI_NODE(node)->self +
DI_NODE(node)->minor_data));
}
pa = (caddr_t)node - DI_NODE(node)->self;
if (DINFOMINOR & DI_ALL(pa)->command)
errno = ENXIO;
else
errno = ENOTSUP;
return (DI_MINOR_NIL);
}
di_node_t
di_minor_devinfo(di_minor_t minor)
{
if (minor == DI_MINOR_NIL) {
errno = EINVAL;
return (DI_NODE_NIL);
}
return (DI_NODE((caddr_t)minor - DI_MINOR(minor)->self +
DI_MINOR(minor)->node));
}
ddi_minor_type
di_minor_type(di_minor_t minor)
{
return (DI_MINOR(minor)->type);
}
char *
di_minor_name(di_minor_t minor)
{
if (DI_MINOR(minor)->name == 0)
return (NULL);
return ((caddr_t)minor - DI_MINOR(minor)->self + DI_MINOR(minor)->name);
}
dev_t
di_minor_devt(di_minor_t minor)
{
return (makedev(DI_MINOR(minor)->dev_major,
DI_MINOR(minor)->dev_minor));
}
int
di_minor_spectype(di_minor_t minor)
{
return (DI_MINOR(minor)->spec_type);
}
char *
di_minor_nodetype(di_minor_t minor)
{
if (DI_MINOR(minor)->node_type == 0)
return (NULL);
return ((caddr_t)minor -
DI_MINOR(minor)->self + DI_MINOR(minor)->node_type);
}
di_prop_t
di_prop_next(di_node_t node, di_prop_t prop)
{
int list = DI_PROP_DRV_LIST;
if (node == DI_NODE_NIL) {
errno = EINVAL;
return (DI_PROP_NIL);
}
if (prop != DI_PROP_NIL)
list = DI_PROP(prop)->prop_list;
do {
switch (list++) {
case DI_PROP_DRV_LIST:
prop = di_prop_drv_next(node, prop);
break;
case DI_PROP_SYS_LIST:
prop = di_prop_sys_next(node, prop);
break;
case DI_PROP_GLB_LIST:
prop = di_prop_global_next(node, prop);
break;
case DI_PROP_HW_LIST:
prop = di_prop_hw_next(node, prop);
break;
default:
errno = EFAULT;
return (DI_PROP_NIL);
}
} while ((prop == DI_PROP_NIL) && (list <= DI_PROP_HW_LIST));
return (prop);
}
dev_t
di_prop_devt(di_prop_t prop)
{
return (makedev(DI_PROP(prop)->dev_major, DI_PROP(prop)->dev_minor));
}
char *
di_prop_name(di_prop_t prop)
{
if (DI_PROP(prop)->prop_name == 0)
return (NULL);
return ((caddr_t)prop - DI_PROP(prop)->self + DI_PROP(prop)->prop_name);
}
int
di_prop_type(di_prop_t prop)
{
uint_t flags = DI_PROP(prop)->prop_flags;
if (flags & DDI_PROP_UNDEF_IT)
return (DI_PROP_TYPE_UNDEF_IT);
if (DI_PROP(prop)->prop_len == 0)
return (DI_PROP_TYPE_BOOLEAN);
if ((flags & DDI_PROP_TYPE_MASK) == DDI_PROP_TYPE_ANY)
return (DI_PROP_TYPE_UNKNOWN);
if (flags & DDI_PROP_TYPE_INT)
return (DI_PROP_TYPE_INT);
if (flags & DDI_PROP_TYPE_INT64)
return (DI_PROP_TYPE_INT64);
if (flags & DDI_PROP_TYPE_STRING)
return (DI_PROP_TYPE_STRING);
if (flags & DDI_PROP_TYPE_BYTE)
return (DI_PROP_TYPE_BYTE);
DPRINTF((DI_ERR, "Unimplemented property type: 0x%x\n", flags));
return (DI_PROP_TYPE_UNKNOWN);
}
extern int di_prop_decode_common(void *prop_data, int len,
int ddi_type, int prom);
int
di_prop_ints(di_prop_t prop, int **prop_data)
{
if (DI_PROP(prop)->prop_len == 0)
return (0);
if ((DI_PROP(prop)->prop_data == 0) ||
(DI_PROP(prop)->prop_data == (di_off_t)-1)) {
errno = EFAULT;
*prop_data = NULL;
return (-1);
}
*prop_data = (int *)((void *)((caddr_t)prop - DI_PROP(prop)->self
+ DI_PROP(prop)->prop_data));
return (di_prop_decode_common((void *)prop_data,
DI_PROP(prop)->prop_len, DI_PROP_TYPE_INT, 0));
}
int
di_prop_int64(di_prop_t prop, int64_t **prop_data)
{
if (DI_PROP(prop)->prop_len == 0)
return (0);
if ((DI_PROP(prop)->prop_data == 0) ||
(DI_PROP(prop)->prop_data == (di_off_t)-1)) {
errno = EFAULT;
*prop_data = NULL;
return (-1);
}
*prop_data = (int64_t *)((void *)((caddr_t)prop - DI_PROP(prop)->self
+ DI_PROP(prop)->prop_data));
return (di_prop_decode_common((void *)prop_data,
DI_PROP(prop)->prop_len, DI_PROP_TYPE_INT64, 0));
}
int
di_prop_strings(di_prop_t prop, char **prop_data)
{
if (DI_PROP(prop)->prop_len == 0)
return (0);
if ((DI_PROP(prop)->prop_data == 0) ||
(DI_PROP(prop)->prop_data == (di_off_t)-1)) {
errno = EFAULT;
*prop_data = NULL;
return (-1);
}
*prop_data = (char *)((caddr_t)prop - DI_PROP(prop)->self
+ DI_PROP(prop)->prop_data);
return (di_prop_decode_common((void *)prop_data,
DI_PROP(prop)->prop_len, DI_PROP_TYPE_STRING, 0));
}
int
di_prop_bytes(di_prop_t prop, uchar_t **prop_data)
{
if (DI_PROP(prop)->prop_len == 0)
return (0);
if ((DI_PROP(prop)->prop_data == 0) ||
(DI_PROP(prop)->prop_data == (di_off_t)-1)) {
errno = EFAULT;
*prop_data = NULL;
return (-1);
}
*prop_data = (uchar_t *)((caddr_t)prop - DI_PROP(prop)->self
+ DI_PROP(prop)->prop_data);
return (di_prop_decode_common((void *)prop_data,
DI_PROP(prop)->prop_len, DI_PROP_TYPE_BYTE, 0));
}
static int
match_prop(di_prop_t prop, dev_t match_dev, const char *name, int type)
{
int prop_type;
#ifdef DEBUG
if (di_prop_name(prop) == NULL) {
DPRINTF((DI_ERR, "libdevinfo: property has no name!\n"));
return (0);
}
#endif
if (strcmp(name, di_prop_name(prop)) != 0)
return (0);
if ((match_dev != DDI_DEV_T_ANY) && (di_prop_devt(prop) != match_dev))
return (0);
prop_type = di_prop_type(prop);
if ((prop_type != DI_PROP_TYPE_UNKNOWN) && (prop_type != type) &&
(prop_type != DI_PROP_TYPE_BOOLEAN))
return (0);
return (1);
}
static di_prop_t
di_prop_search(dev_t match_dev, di_node_t node, const char *name,
int type)
{
di_prop_t prop = DI_PROP_NIL;
if ((node == DI_NODE_NIL) || (name == NULL) || (strlen(name) == 0) ||
(match_dev == DDI_DEV_T_NONE) || !DI_PROP_TYPE_VALID(type)) {
errno = EINVAL;
return (DI_PROP_NIL);
}
while ((prop = di_prop_next(node, prop)) != DI_PROP_NIL) {
DPRINTF((DI_TRACE1, "match prop name %s, devt 0x%lx, type %d\n",
di_prop_name(prop), di_prop_devt(prop),
di_prop_type(prop)));
if (match_prop(prop, match_dev, name, type))
return (prop);
}
return (DI_PROP_NIL);
}
di_prop_t
di_prop_find(dev_t match_dev, di_node_t node, const char *name)
{
di_prop_t prop = DI_PROP_NIL;
if ((node == DI_NODE_NIL) || (name == NULL) || (strlen(name) == 0) ||
(match_dev == DDI_DEV_T_NONE)) {
errno = EINVAL;
return (DI_PROP_NIL);
}
while ((prop = di_prop_next(node, prop)) != DI_PROP_NIL) {
DPRINTF((DI_TRACE1, "found prop name %s, devt 0x%lx, type %d\n",
di_prop_name(prop), di_prop_devt(prop),
di_prop_type(prop)));
if (strcmp(name, di_prop_name(prop)) == 0 &&
(match_dev == DDI_DEV_T_ANY ||
di_prop_devt(prop) == match_dev))
return (prop);
}
return (DI_PROP_NIL);
}
int
di_prop_lookup_ints(dev_t dev, di_node_t node, const char *prop_name,
int **prop_data)
{
di_prop_t prop;
if ((prop = di_prop_search(dev, node, prop_name,
DI_PROP_TYPE_INT)) == DI_PROP_NIL)
return (-1);
return (di_prop_ints(prop, (void *)prop_data));
}
int
di_prop_lookup_int64(dev_t dev, di_node_t node, const char *prop_name,
int64_t **prop_data)
{
di_prop_t prop;
if ((prop = di_prop_search(dev, node, prop_name,
DI_PROP_TYPE_INT64)) == DI_PROP_NIL)
return (-1);
return (di_prop_int64(prop, (void *)prop_data));
}
int
di_prop_lookup_strings(dev_t dev, di_node_t node, const char *prop_name,
char **prop_data)
{
di_prop_t prop;
if ((prop = di_prop_search(dev, node, prop_name,
DI_PROP_TYPE_STRING)) == DI_PROP_NIL)
return (-1);
return (di_prop_strings(prop, (void *)prop_data));
}
int
di_prop_lookup_bytes(dev_t dev, di_node_t node, const char *prop_name,
uchar_t **prop_data)
{
di_prop_t prop;
if ((prop = di_prop_search(dev, node, prop_name,
DI_PROP_TYPE_BYTE)) == DI_PROP_NIL)
return (-1);
return (di_prop_bytes(prop, (void *)prop_data));
}
enum prop_type {
PROP_TYPE_DRV,
PROP_TYPE_SYS,
PROP_TYPE_GLOB,
PROP_TYPE_HW
};
static di_prop_t
di_prop_next_common(di_node_t node, di_prop_t prop, int prop_type)
{
caddr_t pa;
di_off_t prop_off = 0;
if (prop != DI_PROP_NIL) {
if (DI_PROP(prop)->next) {
return (DI_PROP((caddr_t)prop -
DI_PROP(prop)->self + DI_PROP(prop)->next));
} else {
return (DI_PROP_NIL);
}
}
pa = (caddr_t)node - DI_NODE(node)->self;
switch (prop_type) {
case PROP_TYPE_DRV:
prop_off = DI_NODE(node)->drv_prop;
break;
case PROP_TYPE_SYS:
prop_off = DI_NODE(node)->sys_prop;
break;
case PROP_TYPE_HW:
prop_off = DI_NODE(node)->hw_prop;
break;
case PROP_TYPE_GLOB:
prop_off = DI_NODE(node)->glob_prop;
if (prop_off == -1) {
prop_off = 0;
} else if ((prop_off == 0) && (DI_NODE(node)->drv_major >= 0)) {
struct di_devnm *devnm = DI_DEVNM(pa +
DI_ALL(pa)->devnames + (DI_NODE(node)->drv_major *
sizeof (struct di_devnm)));
prop_off = devnm->global_prop;
}
break;
}
if (prop_off) {
return (DI_PROP(pa + prop_off));
}
if (DINFOPROP & DI_ALL(pa)->command)
errno = ENXIO;
else
errno = ENOTSUP;
return (DI_PROP_NIL);
}
di_prop_t
di_prop_drv_next(di_node_t node, di_prop_t prop)
{
return (di_prop_next_common(node, prop, PROP_TYPE_DRV));
}
di_prop_t
di_prop_sys_next(di_node_t node, di_prop_t prop)
{
return (di_prop_next_common(node, prop, PROP_TYPE_SYS));
}
di_prop_t
di_prop_global_next(di_node_t node, di_prop_t prop)
{
return (di_prop_next_common(node, prop, PROP_TYPE_GLOB));
}
di_prop_t
di_prop_hw_next(di_node_t node, di_prop_t prop)
{
return (di_prop_next_common(node, prop, PROP_TYPE_HW));
}
int
di_prop_rawdata(di_prop_t prop, uchar_t **prop_data)
{
#ifdef DEBUG
if (prop == DI_PROP_NIL) {
errno = EINVAL;
return (-1);
}
#endif
if (DI_PROP(prop)->prop_len == 0) {
*prop_data = NULL;
return (0);
}
if ((DI_PROP(prop)->prop_data == 0) ||
(DI_PROP(prop)->prop_data == (di_off_t)-1)) {
errno = EFAULT;
*prop_data = NULL;
return (-1);
}
*prop_data = (uchar_t *)((caddr_t)prop - DI_PROP(prop)->self +
DI_PROP(prop)->prop_data);
return (DI_PROP(prop)->prop_len);
}
di_path_t
di_path_phci_next_path(di_node_t node, di_path_t path)
{
caddr_t pa;
if (path != DI_PATH_NIL) {
if (DI_PATH(path)->path_p_link != 0)
return (DI_PATH((void *)((caddr_t)path -
DI_PATH(path)->self + DI_PATH(path)->path_p_link)));
else {
errno = ENXIO;
return (DI_PATH_NIL);
}
}
if (DI_NODE(node)->multipath_phci != 0) {
DPRINTF((DI_INFO, "phci_next_path: returning %p\n",
((caddr_t)node -
DI_NODE(node)->self + DI_NODE(node)->multipath_phci)));
return (DI_PATH((caddr_t)node - DI_NODE(node)->self +
DI_NODE(node)->multipath_phci));
}
pa = (caddr_t)node - DI_NODE(node)->self;
if (DINFOPATH & (DI_ALL(pa)->command))
errno = ENXIO;
else
errno = ENOTSUP;
return (DI_PATH_NIL);
}
di_path_t
di_path_client_next_path(di_node_t node, di_path_t path)
{
caddr_t pa;
if (path != DI_PATH_NIL) {
if (DI_PATH(path)->path_c_link != 0)
return (DI_PATH((caddr_t)path - DI_PATH(path)->self
+ DI_PATH(path)->path_c_link));
else {
errno = ENXIO;
return (DI_PATH_NIL);
}
}
if (DI_NODE(node)->multipath_client != 0) {
DPRINTF((DI_INFO, "client_next_path: returning %p\n",
((caddr_t)node -
DI_NODE(node)->self + DI_NODE(node)->multipath_client)));
return (DI_PATH((caddr_t)node - DI_NODE(node)->self +
DI_NODE(node)->multipath_client));
}
pa = (caddr_t)node - DI_NODE(node)->self;
if (DINFOPATH & (DI_ALL(pa)->command))
errno = ENXIO;
else
errno = ENOTSUP;
return (DI_PATH_NIL);
}
char *
di_path_addr(di_path_t path, char *buf)
{
caddr_t pa;
pa = (caddr_t)path - DI_PATH(path)->self;
(void) strncpy(buf, (char *)(pa + DI_PATH(path)->path_addr),
MAXPATHLEN);
return (buf);
}
di_path_t
di_path_next(di_node_t node, di_path_t path)
{
if (node == DI_NODE_NIL) {
errno = EINVAL;
return (DI_PATH_NIL);
}
if (DI_NODE(node)->multipath_client) {
return (di_path_client_next_path(node, path));
} else if (DI_NODE(node)->multipath_phci) {
return (di_path_phci_next_path(node, path));
} else {
errno = EINVAL;
return (DI_PATH_NIL);
}
}
di_path_t
di_path_next_phci(di_node_t node, di_path_t path)
{
return (di_path_client_next_path(node, path));
}
di_path_t
di_path_next_client(di_node_t node, di_path_t path)
{
return (di_path_phci_next_path(node, path));
}
di_path_state_t
di_path_state(di_path_t path)
{
return ((di_path_state_t)DI_PATH(path)->path_state);
}
uint_t
di_path_flags(di_path_t path)
{
return (DI_PATH(path)->path_flags);
}
char *
di_path_node_name(di_path_t path)
{
di_node_t client_node;
if ((client_node = di_path_client_node(path)) == NULL)
return (NULL);
return (di_node_name(client_node));
}
char *
di_path_bus_addr(di_path_t path)
{
caddr_t pa = (caddr_t)path - DI_PATH(path)->self;
if (DI_PATH(path)->path_addr == 0)
return (NULL);
return ((char *)(pa + DI_PATH(path)->path_addr));
}
int
di_path_instance(di_path_t path)
{
return (DI_PATH(path)->path_instance);
}
di_node_t
di_path_client_node(di_path_t path)
{
caddr_t pa;
if (path == DI_PATH_NIL) {
errno = EINVAL;
return (DI_PATH_NIL);
}
DPRINTF((DI_TRACE, "Get client node for path %p\n", path));
pa = (caddr_t)path - DI_PATH(path)->self;
if (DI_PATH(path)->path_client) {
return (DI_NODE(pa + DI_PATH(path)->path_client));
}
if ((DI_PATH(path)->path_snap_state & DI_PATH_SNAP_NOCLIENT) == 0)
errno = ENOTSUP;
else
errno = ENXIO;
return (DI_NODE_NIL);
}
di_node_t
di_path_phci_node(di_path_t path)
{
caddr_t pa;
if (path == DI_PATH_NIL) {
errno = EINVAL;
return (DI_PATH_NIL);
}
DPRINTF((DI_TRACE, "Get phci node for path %p\n", path));
pa = (caddr_t)path - DI_PATH(path)->self;
if (DI_PATH(path)->path_phci) {
return (DI_NODE(pa + DI_PATH(path)->path_phci));
}
if ((DI_PATH(path)->path_snap_state & DI_PATH_SNAP_NOPHCI) == 0)
errno = ENOTSUP;
else
errno = ENXIO;
return (DI_NODE_NIL);
}
di_path_prop_t
di_path_prop_next(di_path_t path, di_path_prop_t prop)
{
caddr_t pa;
if (path == DI_PATH_NIL) {
errno = EINVAL;
return (DI_PROP_NIL);
}
if (prop != DI_PROP_NIL) {
if (DI_PROP(prop)->next != 0)
return (DI_PATHPROP((caddr_t)prop -
DI_PROP(prop)->self + DI_PROP(prop)->next));
else {
errno = ENXIO;
return (DI_PROP_NIL);
}
}
pa = (caddr_t)path - DI_PATH(path)->self;
if (DI_PATH(path)->path_prop != 0) {
return (DI_PATHPROP(pa + DI_PATH(path)->path_prop));
}
if (DINFOPROP & (DI_ALL(pa)->command))
errno = ENXIO;
else
errno = ENOTSUP;
return (DI_PROP_NIL);
}
char *
di_path_prop_name(di_path_prop_t prop)
{
caddr_t pa;
pa = (caddr_t)prop - DI_PATHPROP(prop)->self;
return ((char *)(pa + DI_PATHPROP(prop)->prop_name));
}
int
di_path_prop_len(di_path_prop_t prop)
{
return (DI_PATHPROP(prop)->prop_len);
}
int
di_path_prop_type(di_path_prop_t prop)
{
switch (DI_PATHPROP(prop)->prop_type) {
case DDI_PROP_TYPE_INT:
return (DI_PROP_TYPE_INT);
case DDI_PROP_TYPE_INT64:
return (DI_PROP_TYPE_INT64);
case DDI_PROP_TYPE_BYTE:
return (DI_PROP_TYPE_BYTE);
case DDI_PROP_TYPE_STRING:
return (DI_PROP_TYPE_STRING);
}
return (DI_PROP_TYPE_UNKNOWN);
}
int
di_path_prop_bytes(di_path_prop_t prop, uchar_t **prop_data)
{
if ((DI_PATHPROP(prop)->prop_data == 0) ||
(DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) {
errno = EFAULT;
*prop_data = NULL;
return (-1);
}
*prop_data = (uchar_t *)((caddr_t)prop - DI_PATHPROP(prop)->self
+ DI_PATHPROP(prop)->prop_data);
return (di_prop_decode_common((void *)prop_data,
DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_BYTE, 0));
}
int
di_path_prop_ints(di_path_prop_t prop, int **prop_data)
{
if (DI_PATHPROP(prop)->prop_len == 0)
return (0);
if ((DI_PATHPROP(prop)->prop_data == 0) ||
(DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) {
errno = EFAULT;
*prop_data = NULL;
return (-1);
}
*prop_data = (int *)((void *)((caddr_t)prop - DI_PATHPROP(prop)->self
+ DI_PATHPROP(prop)->prop_data));
return (di_prop_decode_common((void *)prop_data,
DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_INT, 0));
}
int
di_path_prop_int64s(di_path_prop_t prop, int64_t **prop_data)
{
if (DI_PATHPROP(prop)->prop_len == 0)
return (0);
if ((DI_PATHPROP(prop)->prop_data == 0) ||
(DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) {
errno = EFAULT;
*prop_data = NULL;
return (-1);
}
*prop_data = (int64_t *)((void *)((caddr_t)prop -
DI_PATHPROP(prop)->self + DI_PATHPROP(prop)->prop_data));
return (di_prop_decode_common((void *)prop_data,
DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_INT64, 0));
}
int
di_path_prop_strings(di_path_prop_t prop, char **prop_data)
{
if (DI_PATHPROP(prop)->prop_len == 0)
return (0);
if ((DI_PATHPROP(prop)->prop_data == 0) ||
(DI_PATHPROP(prop)->prop_data == (di_off_t)-1)) {
errno = EFAULT;
*prop_data = NULL;
return (-1);
}
*prop_data = (char *)((caddr_t)prop - DI_PATHPROP(prop)->self
+ DI_PATHPROP(prop)->prop_data);
return (di_prop_decode_common((void *)prop_data,
DI_PATHPROP(prop)->prop_len, DI_PROP_TYPE_STRING, 0));
}
static di_path_prop_t
di_path_prop_search(di_path_t path, const char *name, int type)
{
di_path_prop_t prop = DI_PROP_NIL;
if ((path == DI_PATH_NIL) || (name == NULL) || (strlen(name) == 0) ||
!DI_PROP_TYPE_VALID(type)) {
errno = EINVAL;
return (DI_PROP_NIL);
}
while ((prop = di_path_prop_next(path, prop)) != DI_PROP_NIL) {
int prop_type = di_path_prop_type(prop);
DPRINTF((DI_TRACE1, "match path prop name %s, type %d\n",
di_path_prop_name(prop), prop_type));
if (strcmp(name, di_path_prop_name(prop)) != 0)
continue;
if ((prop_type != DI_PROP_TYPE_UNKNOWN) && (prop_type != type))
continue;
return (prop);
}
return (DI_PROP_NIL);
}
int
di_path_prop_lookup_bytes(di_path_t path, const char *prop_name,
uchar_t **prop_data)
{
di_path_prop_t prop;
if ((prop = di_path_prop_search(path, prop_name,
DI_PROP_TYPE_BYTE)) == DI_PROP_NIL)
return (-1);
return (di_path_prop_bytes(prop, prop_data));
}
int
di_path_prop_lookup_ints(di_path_t path, const char *prop_name,
int **prop_data)
{
di_path_prop_t prop;
if ((prop = di_path_prop_search(path, prop_name,
DI_PROP_TYPE_INT)) == DI_PROP_NIL)
return (-1);
return (di_path_prop_ints(prop, prop_data));
}
int
di_path_prop_lookup_int64s(di_path_t path, const char *prop_name,
int64_t **prop_data)
{
di_path_prop_t prop;
if ((prop = di_path_prop_search(path, prop_name,
DI_PROP_TYPE_INT64)) == DI_PROP_NIL)
return (-1);
return (di_path_prop_int64s(prop, prop_data));
}
int di_path_prop_lookup_strings(di_path_t path, const char *prop_name,
char **prop_data)
{
di_path_prop_t prop;
if ((prop = di_path_prop_search(path, prop_name,
DI_PROP_TYPE_STRING)) == DI_PROP_NIL)
return (-1);
return (di_path_prop_strings(prop, prop_data));
}
di_node_t
di_vhci_first_node(di_node_t root)
{
struct di_all *dap;
caddr_t pa;
DPRINTF((DI_INFO, "Get first vhci node\n"));
if (root == DI_NODE_NIL) {
errno = EINVAL;
return (DI_NODE_NIL);
}
pa = (caddr_t)root - DI_NODE(root)->self;
dap = DI_ALL(pa);
if (dap->top_vhci_devinfo == 0) {
errno = ENXIO;
return (DI_NODE_NIL);
}
return (DI_NODE(pa + dap->top_vhci_devinfo));
}
di_node_t
di_vhci_next_node(di_node_t node)
{
caddr_t pa;
if (node == DI_NODE_NIL) {
errno = EINVAL;
return (DI_NODE_NIL);
}
DPRINTF((DI_TRACE, "next vhci node on the snap shot:"
" current=%s\n", di_node_name(node)));
if (DI_NODE(node)->next_vhci == 0) {
errno = ENXIO;
return (DI_NODE_NIL);
}
pa = (caddr_t)node - DI_NODE(node)->self;
return (DI_NODE(pa + DI_NODE(node)->next_vhci));
}
di_node_t
di_phci_first_node(di_node_t vhci_node)
{
caddr_t pa;
DPRINTF((DI_INFO, "Get first phci node:\n"
" current=%s", di_node_name(vhci_node)));
if (vhci_node == DI_NODE_NIL) {
errno = EINVAL;
return (DI_NODE_NIL);
}
pa = (caddr_t)vhci_node - DI_NODE(vhci_node)->self;
if (DI_NODE(vhci_node)->top_phci == 0) {
errno = ENXIO;
return (DI_NODE_NIL);
}
return (DI_NODE(pa + DI_NODE(vhci_node)->top_phci));
}
di_node_t
di_phci_next_node(di_node_t node)
{
caddr_t pa;
if (node == DI_NODE_NIL) {
errno = EINVAL;
return (DI_NODE_NIL);
}
DPRINTF((DI_TRACE, "next phci node on the snap shot:"
" current=%s\n", di_node_name(node)));
if (DI_NODE(node)->next_phci == 0) {
errno = ENXIO;
return (DI_NODE_NIL);
}
pa = (caddr_t)node - DI_NODE(node)->self;
return (DI_NODE(pa + DI_NODE(node)->next_phci));
}
void *
di_parent_private_data(di_node_t node)
{
caddr_t pa;
if (DI_NODE(node)->parent_data == 0) {
errno = ENXIO;
return (NULL);
}
if (DI_NODE(node)->parent_data == (di_off_t)-1) {
errno = EFAULT;
return (NULL);
}
pa = (caddr_t)node - DI_NODE(node)->self;
if (DI_NODE(node)->parent_data)
return (pa + DI_NODE(node)->parent_data);
if (DI_ALL(pa)->command & DINFOPRIVDATA)
errno = ENXIO;
else
errno = ENOTSUP;
return (NULL);
}
void *
di_driver_private_data(di_node_t node)
{
caddr_t pa;
if (DI_NODE(node)->driver_data == 0) {
errno = ENXIO;
return (NULL);
}
if (DI_NODE(node)->driver_data == (di_off_t)-1) {
errno = EFAULT;
return (NULL);
}
pa = (caddr_t)node - DI_NODE(node)->self;
if (DI_NODE(node)->driver_data)
return (pa + DI_NODE(node)->driver_data);
if (DI_ALL(pa)->command & DINFOPRIVDATA)
errno = ENXIO;
else
errno = ENOTSUP;
return (NULL);
}
typedef struct {
void *arg;
const char *type;
uint_t flag;
int (*hp_callback)(di_node_t, di_hp_t, void *);
} di_walk_hp_arg_t;
static int
di_walk_hp_callback(di_node_t node, void *argp)
{
di_walk_hp_arg_t *arg = (di_walk_hp_arg_t *)argp;
di_hp_t hp;
char *type_str;
for (hp = DI_HP_NIL; (hp = di_hp_next(node, hp)) != DI_HP_NIL; ) {
if (arg->type != NULL) {
type_str = di_hp_description(hp);
if (type_str && (strcmp(arg->type, type_str) != 0))
continue;
}
if (!(arg->flag & DI_HP_PORT) &&
(di_hp_type(hp) == DDI_HP_CN_TYPE_VIRTUAL_PORT))
continue;
if (!(arg->flag & DI_HP_CONNECTOR) &&
!(di_hp_type(hp) == DDI_HP_CN_TYPE_VIRTUAL_PORT))
continue;
if (arg->hp_callback(node, hp, arg->arg) != DI_WALK_CONTINUE)
return (DI_WALK_TERMINATE);
}
return (DI_WALK_CONTINUE);
}
int
di_walk_hp(di_node_t node, const char *type, uint_t flag, void *arg,
int (*hp_callback)(di_node_t node, di_hp_t hp, void *arg))
{
di_walk_hp_arg_t walk_arg;
caddr_t pa;
#ifdef DEBUG
char *devfspath = di_devfs_path(node);
DPRINTF((DI_INFO, "walking hotplug nodes under %s\n", devfspath));
di_devfs_path_free(devfspath);
#endif
if ((node == DI_NODE_NIL) || (hp_callback == NULL)) {
errno = EINVAL;
return (-1);
}
pa = (caddr_t)node - DI_NODE(node)->self;
if (!(DI_ALL(pa)->command & DINFOHP)) {
errno = ENOTSUP;
return (-1);
}
walk_arg.arg = arg;
walk_arg.type = type;
walk_arg.flag = flag;
walk_arg.hp_callback = hp_callback;
return (di_walk_node(node, DI_WALK_CLDFIRST, &walk_arg,
di_walk_hp_callback));
}
di_hp_t
di_hp_next(di_node_t node, di_hp_t hp)
{
caddr_t pa;
if (node == DI_NODE_NIL) {
errno = EINVAL;
return (DI_HP_NIL);
}
if (hp != DI_HP_NIL) {
if (DI_HP(hp)->next != 0)
return (DI_HP((caddr_t)hp - hp->self + hp->next));
else {
errno = ENXIO;
return (DI_HP_NIL);
}
}
if (DI_NODE(node)->hp_data != 0) {
return (DI_HP((caddr_t)node - DI_NODE(node)->self +
DI_NODE(node)->hp_data));
}
pa = (caddr_t)node - DI_NODE(node)->self;
if (DINFOHP & DI_ALL(pa)->command)
errno = ENXIO;
else
errno = ENOTSUP;
return (DI_HP_NIL);
}
char *
di_hp_name(di_hp_t hp)
{
caddr_t pa;
if (hp == DI_HP_NIL) {
errno = EINVAL;
return (NULL);
}
pa = (caddr_t)hp - DI_HP(hp)->self;
if (DI_HP(hp)->hp_name == 0) {
errno = ENXIO;
return (NULL);
}
return ((char *)(pa + DI_HP(hp)->hp_name));
}
int
di_hp_connection(di_hp_t hp)
{
if (hp == DI_HP_NIL) {
errno = EINVAL;
return (-1);
}
if (DI_HP(hp)->hp_connection == -1)
errno = ENOENT;
return (DI_HP(hp)->hp_connection);
}
int
di_hp_depends_on(di_hp_t hp)
{
if (hp == DI_HP_NIL) {
errno = EINVAL;
return (-1);
}
if (DI_HP(hp)->hp_depends_on == -1)
errno = ENOENT;
return (DI_HP(hp)->hp_depends_on);
}
int
di_hp_state(di_hp_t hp)
{
if (hp == DI_HP_NIL) {
errno = EINVAL;
return (-1);
}
return (DI_HP(hp)->hp_state);
}
int
di_hp_type(di_hp_t hp)
{
if (hp == DI_HP_NIL) {
errno = EINVAL;
return (-1);
}
return (DI_HP(hp)->hp_type);
}
char *
di_hp_description(di_hp_t hp)
{
caddr_t pa;
if (hp == DI_HP_NIL) {
errno = EINVAL;
return (NULL);
}
pa = (caddr_t)hp - DI_HP(hp)->self;
if (DI_HP(hp)->hp_type_str == 0)
return (NULL);
return ((char *)(pa + DI_HP(hp)->hp_type_str));
}
di_node_t
di_hp_child(di_hp_t hp)
{
caddr_t pa;
if (hp == DI_HP_NIL) {
errno = EINVAL;
return (DI_NODE_NIL);
}
pa = (caddr_t)hp - DI_HP(hp)->self;
if (DI_HP(hp)->hp_child > 0) {
return (DI_NODE(pa + DI_HP(hp)->hp_child));
}
if (!(DINFOSUBTREE & DI_ALL(pa)->command))
errno = ENOTSUP;
else
errno = ENXIO;
return (DI_NODE_NIL);
}
time_t
di_hp_last_change(di_hp_t hp)
{
if (hp == DI_HP_NIL) {
errno = EINVAL;
return ((time_t)0);
}
return ((time_t)DI_HP(hp)->hp_last_change);
}
#define OBP_MAXBUF OPROMMAXPARAM - sizeof (int)
#define OBP_MAXPROPLEN OBP_MAXBUF - OBP_MAXPROPNAME;
struct di_prom_prop {
char *name;
int len;
uchar_t *data;
struct di_prom_prop *next;
};
struct di_prom_handle {
mutex_t lock;
int fd;
struct di_prom_prop *list;
union {
char buf[OPROMMAXPARAM];
struct openpromio opp;
} oppbuf;
};
di_prom_handle_t
di_prom_init()
{
struct di_prom_handle *p;
if ((p = malloc(sizeof (struct di_prom_handle))) == NULL)
return (DI_PROM_HANDLE_NIL);
DPRINTF((DI_INFO, "di_prom_init: get prom handle 0x%p\n", p));
(void) mutex_init(&p->lock, USYNC_THREAD, NULL);
if ((p->fd = open("/dev/openprom", O_RDONLY)) < 0) {
free(p);
return (DI_PROM_HANDLE_NIL);
}
p->list = NULL;
return ((di_prom_handle_t)p);
}
static void
di_prom_prop_free(struct di_prom_prop *list)
{
struct di_prom_prop *tmp = list;
while (tmp != NULL) {
list = tmp->next;
if (tmp->name != NULL) {
free(tmp->name);
}
if (tmp->data != NULL) {
free(tmp->data);
}
free(tmp);
tmp = list;
}
}
void
di_prom_fini(di_prom_handle_t ph)
{
struct di_prom_handle *p = (struct di_prom_handle *)ph;
DPRINTF((DI_INFO, "di_prom_fini: free prom handle 0x%p\n", p));
(void) close(p->fd);
(void) mutex_destroy(&p->lock);
di_prom_prop_free(p->list);
free(p);
}
static di_prom_prop_t
di_prom_prop_found(di_prom_handle_t ph, int nodeid,
di_prom_prop_t prom_prop)
{
struct di_prom_handle *p = (struct di_prom_handle *)ph;
struct openpromio *opp = &p->oppbuf.opp;
int *ip = (int *)((void *)opp->oprom_array);
struct di_prom_prop *prop = (struct di_prom_prop *)prom_prop;
DPRINTF((DI_TRACE1, "Looking for nodeid 0x%x\n", nodeid));
opp->oprom_size = sizeof (int);
*ip = nodeid;
if (ioctl(p->fd, OPROMSETNODEID, opp) < 0) {
DPRINTF((DI_ERR, "*** Nodeid not found 0x%x\n", nodeid));
return (DI_PROM_PROP_NIL);
}
DPRINTF((DI_TRACE, "Found nodeid 0x%x\n", nodeid));
bzero(opp, OBP_MAXBUF);
opp->oprom_size = OBP_MAXPROPNAME;
if (prom_prop != DI_PROM_PROP_NIL)
(void) strcpy(opp->oprom_array, prop->name);
if ((ioctl(p->fd, OPROMNXTPROP, opp) < 0) || (opp->oprom_size == 0))
return (DI_PROM_PROP_NIL);
if ((prop = malloc(sizeof (struct di_prom_prop))) == NULL)
return (DI_PROM_PROP_NIL);
if ((prop->name = strdup(opp->oprom_array)) == NULL) {
free(prop);
return (DI_PROM_PROP_NIL);
}
opp->oprom_size = OBP_MAXPROPLEN;
if ((ioctl(p->fd, OPROMGETPROP, opp) < 0) ||
(opp->oprom_size == (uint_t)-1)) {
free(prop->name);
free(prop);
return (DI_PROM_PROP_NIL);
}
prop->len = opp->oprom_size;
if (prop->len == 0)
prop->data = NULL;
else if ((prop->data = malloc(prop->len)) == NULL) {
free(prop->name);
free(prop);
return (DI_PROM_PROP_NIL);
}
bcopy(opp->oprom_array, prop->data, prop->len);
prop->next = p->list;
p->list = prop;
return ((di_prom_prop_t)prop);
}
di_prom_prop_t
di_prom_prop_next(di_prom_handle_t ph, di_node_t node, di_prom_prop_t prom_prop)
{
struct di_prom_handle *p = (struct di_prom_handle *)ph;
DPRINTF((DI_TRACE1, "Search next prop for node 0x%p with ph 0x%p\n",
node, p));
if ((ph == DI_PROM_HANDLE_NIL) || (node == DI_NODE_NIL)) {
errno = EINVAL;
return (DI_PROM_PROP_NIL);
}
if (di_nodeid(node) != DI_PROM_NODEID) {
errno = ENXIO;
return (DI_PROM_PROP_NIL);
}
(void) mutex_lock(&p->lock);
prom_prop = di_prom_prop_found(ph, DI_NODE(node)->nodeid, prom_prop);
(void) mutex_unlock(&p->lock);
return (prom_prop);
}
char *
di_prom_prop_name(di_prom_prop_t prom_prop)
{
if (prom_prop == DI_PROM_PROP_NIL) {
errno = EINVAL;
return (NULL);
}
return (((struct di_prom_prop *)prom_prop)->name);
}
int
di_prom_prop_data(di_prom_prop_t prom_prop, uchar_t **prom_prop_data)
{
if (prom_prop == DI_PROM_PROP_NIL) {
errno = EINVAL;
return (0);
}
*prom_prop_data = ((struct di_prom_prop *)prom_prop)->data;
return (((struct di_prom_prop *)prom_prop)->len);
}
static struct di_prom_prop *
di_prom_prop_lookup_common(di_prom_handle_t ph, di_node_t node,
const char *prom_prop_name)
{
struct openpromio *opp;
struct di_prom_prop *prop;
struct di_prom_handle *p = (struct di_prom_handle *)ph;
if ((ph == DI_PROM_HANDLE_NIL) || (node == DI_NODE_NIL)) {
errno = EINVAL;
return (NULL);
}
if (di_nodeid(node) != DI_PROM_NODEID) {
errno = ENXIO;
return (NULL);
}
opp = &p->oppbuf.opp;
(void) mutex_lock(&p->lock);
opp->oprom_size = sizeof (int);
opp->oprom_node = DI_NODE(node)->nodeid;
if (ioctl(p->fd, OPROMSETNODEID, opp) < 0) {
errno = ENXIO;
DPRINTF((DI_ERR, "*** Nodeid not found 0x%x\n",
DI_NODE(node)->nodeid));
(void) mutex_unlock(&p->lock);
return (NULL);
}
bzero(opp, OBP_MAXBUF);
opp->oprom_size = OBP_MAXPROPLEN;
(void) strcpy(opp->oprom_array, prom_prop_name);
if ((ioctl(p->fd, OPROMGETPROPLEN, opp) < 0) ||
(opp->oprom_len == -1)) {
(void) mutex_unlock(&p->lock);
return (NULL);
}
if ((prop = malloc(sizeof (struct di_prom_prop))) == NULL) {
(void) mutex_unlock(&p->lock);
return (NULL);
}
prop->name = NULL;
prop->len = opp->oprom_len;
if (prop->len == 0) {
prop->data = NULL;
prop->next = p->list;
p->list = prop;
(void) mutex_unlock(&p->lock);
return (prop);
}
bzero(opp, OBP_MAXBUF);
opp->oprom_size = OBP_MAXPROPLEN;
(void) strcpy(opp->oprom_array, prom_prop_name);
if ((ioctl(p->fd, OPROMGETPROP, opp) < 0) ||
(opp->oprom_size == (uint_t)-1)) {
(void) mutex_unlock(&p->lock);
free(prop);
return (NULL);
}
if ((prop->data = malloc(prop->len)) == NULL) {
(void) mutex_unlock(&p->lock);
free(prop);
return (NULL);
}
bcopy(opp->oprom_array, prop->data, prop->len);
prop->next = p->list;
p->list = prop;
(void) mutex_unlock(&p->lock);
return (prop);
}
int
di_prom_prop_lookup_ints(di_prom_handle_t ph, di_node_t node,
const char *prom_prop_name, int **prom_prop_data)
{
int len;
struct di_prom_prop *prop;
prop = di_prom_prop_lookup_common(ph, node, prom_prop_name);
if (prop == NULL) {
*prom_prop_data = NULL;
return (-1);
}
if (prop->len == 0) {
*prom_prop_data = NULL;
return (0);
}
len = di_prop_decode_common((void *)&prop->data, prop->len,
DI_PROP_TYPE_INT, 1);
*prom_prop_data = (int *)((void *)prop->data);
return (len);
}
int
di_prom_prop_lookup_strings(di_prom_handle_t ph, di_node_t node,
const char *prom_prop_name, char **prom_prop_data)
{
int len;
struct di_prom_prop *prop;
prop = di_prom_prop_lookup_common(ph, node, prom_prop_name);
if (prop == NULL) {
*prom_prop_data = NULL;
return (-1);
}
if (prop->len == 0) {
*prom_prop_data = NULL;
return (0);
}
if (((char *)prop->data)[prop->len - 1] != '\0') {
uchar_t *tmp;
prop->len++;
if ((tmp = realloc(prop->data, prop->len)) == NULL)
return (-1);
prop->data = tmp;
((char *)prop->data)[prop->len - 1] = '\0';
DPRINTF((DI_INFO, "OBP string not NULL terminated: "
"node=%s, prop=%s, val=%s\n",
di_node_name(node), prom_prop_name, prop->data));
}
len = di_prop_decode_common((void *)&prop->data, prop->len,
DI_PROP_TYPE_STRING, 1);
*prom_prop_data = (char *)prop->data;
return (len);
}
int
di_prom_prop_lookup_bytes(di_prom_handle_t ph, di_node_t node,
const char *prom_prop_name, uchar_t **prom_prop_data)
{
int len;
struct di_prom_prop *prop;
prop = di_prom_prop_lookup_common(ph, node, prom_prop_name);
if (prop == NULL) {
*prom_prop_data = NULL;
return (-1);
}
if (prop->len == 0) {
*prom_prop_data = NULL;
return (0);
}
len = di_prop_decode_common((void *)&prop->data, prop->len,
DI_PROP_TYPE_BYTE, 1);
*prom_prop_data = prop->data;
return (len);
}
int
di_prop_slot_names(di_prop_t prop, di_slot_name_t **prop_data)
{
int rawlen, count;
uchar_t *rawdata;
char *nm = di_prop_name(prop);
if (nm == NULL || strcmp(DI_PROP_SLOT_NAMES, nm) != 0)
goto ERROUT;
rawlen = di_prop_rawdata(prop, &rawdata);
if (rawlen <= 0 || rawdata == NULL)
goto ERROUT;
count = di_slot_names_decode(rawdata, rawlen, prop_data);
if (count < 0 || *prop_data == NULL)
goto ERROUT;
return (count);
ERROUT:
errno = EFAULT;
*prop_data = NULL;
return (-1);
}
int
di_prop_lookup_slot_names(dev_t dev, di_node_t node,
di_slot_name_t **prop_data)
{
di_prop_t prop;
if ((prop = di_prop_find(dev, node, DI_PROP_SLOT_NAMES)) ==
DI_PROP_NIL) {
*prop_data = NULL;
return (-1);
}
return (di_prop_slot_names(prop, (void *)prop_data));
}
int
di_prom_prop_slot_names(di_prom_prop_t prom_prop, di_slot_name_t **prop_data)
{
int rawlen, count;
uchar_t *rawdata;
rawlen = di_prom_prop_data(prom_prop, &rawdata);
if (rawlen <= 0 || rawdata == NULL)
goto ERROUT;
count = di_slot_names_decode(rawdata, rawlen, prop_data);
if (count < 0 || *prop_data == NULL)
goto ERROUT;
return (count);
ERROUT:
errno = EFAULT;
*prop_data = NULL;
return (-1);
}
int
di_prom_prop_lookup_slot_names(di_prom_handle_t ph, di_node_t node,
di_slot_name_t **prop_data)
{
struct di_prom_prop *prom_prop;
prom_prop = di_prom_prop_lookup_common(ph, node, DI_PROP_SLOT_NAMES);
if (prom_prop == NULL) {
*prop_data = NULL;
return (-1);
}
return (di_prom_prop_slot_names(prom_prop, prop_data));
}
di_lnode_t
di_link_to_lnode(di_link_t link, uint_t endpoint)
{
struct di_all *di_all;
if ((link == DI_LINK_NIL) ||
((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) {
errno = EINVAL;
return (DI_LNODE_NIL);
}
di_all = DI_ALL((caddr_t)link - DI_LINK(link)->self);
if (endpoint == DI_LINK_SRC) {
return (DI_LNODE((caddr_t)di_all + DI_LINK(link)->src_lnode));
} else {
return (DI_LNODE((caddr_t)di_all + DI_LINK(link)->tgt_lnode));
}
}
char *
di_lnode_name(di_lnode_t lnode)
{
return (di_driver_name(di_lnode_devinfo(lnode)));
}
di_node_t
di_lnode_devinfo(di_lnode_t lnode)
{
struct di_all *di_all;
di_all = DI_ALL((caddr_t)lnode - DI_LNODE(lnode)->self);
return (DI_NODE((caddr_t)di_all + DI_LNODE(lnode)->node));
}
int
di_lnode_devt(di_lnode_t lnode, dev_t *devt)
{
if ((lnode == DI_LNODE_NIL) || (devt == NULL)) {
errno = EINVAL;
return (-1);
}
if ((DI_LNODE(lnode)->dev_major == (major_t)-1) &&
(DI_LNODE(lnode)->dev_minor == (minor_t)-1))
return (-1);
*devt = makedev(DI_LNODE(lnode)->dev_major, DI_LNODE(lnode)->dev_minor);
return (0);
}
int
di_link_spectype(di_link_t link)
{
return (DI_LINK(link)->spec_type);
}
void
di_minor_private_set(di_minor_t minor, void *data)
{
DI_MINOR(minor)->user_private_data = (uintptr_t)data;
}
void *
di_minor_private_get(di_minor_t minor)
{
return ((void *)(uintptr_t)DI_MINOR(minor)->user_private_data);
}
void
di_node_private_set(di_node_t node, void *data)
{
DI_NODE(node)->user_private_data = (uintptr_t)data;
}
void *
di_node_private_get(di_node_t node)
{
return ((void *)(uintptr_t)DI_NODE(node)->user_private_data);
}
void
di_path_private_set(di_path_t path, void *data)
{
DI_PATH(path)->user_private_data = (uintptr_t)data;
}
void *
di_path_private_get(di_path_t path)
{
return ((void *)(uintptr_t)DI_PATH(path)->user_private_data);
}
void
di_lnode_private_set(di_lnode_t lnode, void *data)
{
DI_LNODE(lnode)->user_private_data = (uintptr_t)data;
}
void *
di_lnode_private_get(di_lnode_t lnode)
{
return ((void *)(uintptr_t)DI_LNODE(lnode)->user_private_data);
}
void
di_link_private_set(di_link_t link, void *data)
{
DI_LINK(link)->user_private_data = (uintptr_t)data;
}
void *
di_link_private_get(di_link_t link)
{
return ((void *)(uintptr_t)DI_LINK(link)->user_private_data);
}
di_lnode_t
di_lnode_next(di_node_t node, di_lnode_t lnode)
{
struct di_all *di_all;
if (node == DI_NODE_NIL) {
errno = EINVAL;
return (DI_LNODE_NIL);
}
di_all = DI_ALL((caddr_t)node - DI_NODE(node)->self);
if (lnode == DI_NODE_NIL) {
if (DI_NODE(node)->lnodes != 0)
return (DI_LNODE((caddr_t)di_all +
DI_NODE(node)->lnodes));
} else {
if (DI_LNODE(lnode)->node_next != 0)
return (DI_LNODE((caddr_t)di_all +
DI_LNODE(lnode)->node_next));
}
if (DINFOLYR & DI_ALL(di_all)->command)
errno = ENXIO;
else
errno = ENOTSUP;
return (DI_LNODE_NIL);
}
di_link_t
di_link_next_by_node(di_node_t node, di_link_t link, uint_t endpoint)
{
struct di_all *di_all;
if ((node == DI_NODE_NIL) ||
((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) {
errno = EINVAL;
return (DI_LINK_NIL);
}
di_all = DI_ALL((caddr_t)node - DI_NODE(node)->self);
if (endpoint == DI_LINK_SRC) {
if (link == DI_LINK_NIL) {
if (DI_NODE(node)->src_links != 0)
return (DI_LINK((caddr_t)di_all +
DI_NODE(node)->src_links));
} else {
if (DI_LINK(link)->src_node_next != 0)
return (DI_LINK((caddr_t)di_all +
DI_LINK(link)->src_node_next));
}
} else {
if (link == DI_LINK_NIL) {
if (DI_NODE(node)->tgt_links != 0)
return (DI_LINK((caddr_t)di_all +
DI_NODE(node)->tgt_links));
} else {
if (DI_LINK(link)->tgt_node_next != 0)
return (DI_LINK((caddr_t)di_all +
DI_LINK(link)->tgt_node_next));
}
}
if (DINFOLYR & DI_ALL(di_all)->command)
errno = ENXIO;
else
errno = ENOTSUP;
return (DI_LINK_NIL);
}
di_link_t
di_link_next_by_lnode(di_lnode_t lnode, di_link_t link, uint_t endpoint)
{
struct di_all *di_all;
if ((lnode == DI_LNODE_NIL) ||
((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) {
errno = EINVAL;
return (DI_LINK_NIL);
}
di_all = DI_ALL((caddr_t)lnode - DI_LNODE(lnode)->self);
if (endpoint == DI_LINK_SRC) {
if (link == DI_LINK_NIL) {
if (DI_LNODE(lnode)->link_out == 0)
return (DI_LINK_NIL);
return (DI_LINK((caddr_t)di_all +
DI_LNODE(lnode)->link_out));
} else {
if (DI_LINK(link)->src_link_next == 0)
return (DI_LINK_NIL);
return (DI_LINK((caddr_t)di_all +
DI_LINK(link)->src_link_next));
}
} else {
if (link == DI_LINK_NIL) {
if (DI_LNODE(lnode)->link_in == 0)
return (DI_LINK_NIL);
return (DI_LINK((caddr_t)di_all +
DI_LNODE(lnode)->link_in));
} else {
if (DI_LINK(link)->tgt_link_next == 0)
return (DI_LINK_NIL);
return (DI_LINK((caddr_t)di_all +
DI_LINK(link)->tgt_link_next));
}
}
}
static void
walk_one_link(struct node_list **headp, uint_t ep,
void *arg, int (*callback)(di_link_t link, void *arg))
{
int action = DI_WALK_CONTINUE;
di_link_t link = DI_LINK_NIL;
di_node_t node = (*headp)->node;
while ((link = di_link_next_by_node(node, link, ep)) != DI_LINK_NIL) {
action = callback(link, arg);
if (action == DI_WALK_TERMINATE) {
break;
}
}
update_node_list(action, DI_WALK_LINKGEN, headp);
}
int
di_walk_link(di_node_t root, uint_t flag, uint_t endpoint, void *arg,
int (*link_callback)(di_link_t link, void *arg))
{
struct node_list *head;
#ifdef DEBUG
char *devfspath = di_devfs_path(root);
DPRINTF((DI_INFO, "walking %s link data under %s\n",
(endpoint == DI_LINK_SRC) ? "src" : "tgt", devfspath));
di_devfs_path_free(devfspath);
#endif
if ((root == DI_NODE_NIL) || (link_callback == NULL) || (flag != 0) ||
((endpoint != DI_LINK_SRC) && (endpoint != DI_LINK_TGT))) {
errno = EINVAL;
return (-1);
}
if ((head = malloc(sizeof (struct node_list))) == NULL) {
DPRINTF((DI_ERR, "malloc of node_list failed\n"));
return (-1);
}
head->next = NULL;
head->node = root;
DPRINTF((DI_INFO, "Start link data walking from node %s\n",
di_node_name(root)));
while (head != NULL)
walk_one_link(&head, endpoint, arg, link_callback);
return (0);
}
static void
walk_one_lnode(struct node_list **headp, void *arg,
int (*callback)(di_lnode_t lnode, void *arg))
{
int action = DI_WALK_CONTINUE;
di_lnode_t lnode = DI_LNODE_NIL;
di_node_t node = (*headp)->node;
while ((lnode = di_lnode_next(node, lnode)) != DI_LNODE_NIL) {
action = callback(lnode, arg);
if (action == DI_WALK_TERMINATE) {
break;
}
}
update_node_list(action, DI_WALK_LINKGEN, headp);
}
int
di_walk_lnode(di_node_t root, uint_t flag, void *arg,
int (*lnode_callback)(di_lnode_t lnode, void *arg))
{
struct node_list *head;
#ifdef DEBUG
char *devfspath = di_devfs_path(root);
DPRINTF((DI_INFO, "walking lnode data under %s\n", devfspath));
di_devfs_path_free(devfspath);
#endif
if ((root == DI_NODE_NIL) || (lnode_callback == NULL) || (flag != 0)) {
errno = EINVAL;
return (-1);
}
if ((head = malloc(sizeof (struct node_list))) == NULL) {
DPRINTF((DI_ERR, "malloc of node_list failed\n"));
return (-1);
}
head->next = NULL;
head->node = root;
DPRINTF((DI_INFO, "Start lnode data walking from node %s\n",
di_node_name(root)));
while (head != NULL)
walk_one_lnode(&head, arg, lnode_callback);
return (0);
}
static char *
alias_to_curr(di_node_t anynode, char *devfspath, di_node_t *nodep)
{
caddr_t pa;
struct di_all *all;
struct di_alias *di_alias;
di_node_t node;
char *curr;
char *cp;
char *alias;
di_off_t off;
char buf[MAXPATHLEN];
*nodep = NULL;
if (anynode == DI_NODE_NIL || devfspath == NULL)
return (NULL);
pa = (caddr_t)anynode - DI_NODE(anynode)->self;
all = DI_ALL(pa);
di_alias = NULL;
for (off = all->aliases; off > 0; off = di_alias->next) {
di_alias = DI_ALIAS(pa + off);
alias = di_alias->alias;
if (strncmp(devfspath, alias, strlen(alias)) == 0) {
cp = devfspath + strlen(alias);
node = DI_NODE(pa + di_alias->curroff);
assert(node != DI_NODE_NIL);
if (*cp == '\0') {
*nodep = node;
return (NULL);
} else if (*cp == '/') {
curr = di_devfs_path(node);
(void) snprintf(buf, sizeof (buf), "%s%s",
curr, cp);
di_devfs_path_free(curr);
curr = strdup(buf);
return (curr);
}
}
}
return (NULL);
}
static di_node_t
di_lookup_node_impl(di_node_t root, char *devfspath)
{
struct di_all *dap;
di_node_t node;
char *copy, *slash, *pname, *paddr;
if (*devfspath != '/' || strstr(devfspath, "//")) {
DPRINTF((DI_ERR, "Invalid path: %s\n", devfspath));
return (DI_NODE_NIL);
}
if (root == DI_NODE_NIL) {
DPRINTF((DI_ERR, "root node is DI_NODE_NIL\n"));
return (DI_NODE_NIL);
}
dap = DI_ALL((caddr_t)root - DI_NODE(root)->self);
if (strcmp(dap->root_path, "/") != 0) {
DPRINTF((DI_ERR, "snapshot root not / : %s\n", dap->root_path));
return (DI_NODE_NIL);
}
if ((copy = strdup(devfspath)) == NULL) {
DPRINTF((DI_ERR, "strdup failed on: %s\n", devfspath));
return (DI_NODE_NIL);
}
for (slash = copy, node = root; slash; ) {
if (*(slash + 1) == '\0')
break;
pname = slash + 1;
node = di_child_node(node);
if (slash = strchr(pname, '/'))
*slash = '\0';
if (paddr = strchr(pname, '@'))
*paddr++ = '\0';
for (; node != DI_NODE_NIL; node = di_sibling_node(node)) {
char *name, *baddr;
name = di_node_name(node);
baddr = di_bus_addr(node);
if (strcmp(pname, name) != 0)
continue;
if (paddr && baddr && strcmp(paddr, baddr) == 0)
break;
if (paddr == NULL && (baddr == NULL || *baddr == '\0'))
break;
}
if (node == DI_NODE_NIL) {
DPRINTF((DI_ERR, "%s@%s: no node\n", pname, paddr));
free(copy);
return (DI_NODE_NIL);
}
}
assert(node != DI_NODE_NIL);
free(copy);
return (node);
}
di_node_t
di_lookup_node(di_node_t root, char *devfspath)
{
di_node_t node;
char *curr;
node = di_lookup_node_impl(root, devfspath);
if (node != DI_NODE_NIL) {
return (node);
}
curr = alias_to_curr(root, devfspath, &node);
if (curr == NULL) {
return (node);
}
node = di_lookup_node_impl(root, curr);
free(curr);
return (node);
}
char *
di_alias2curr(di_node_t anynode, char *alias)
{
di_node_t currnode = DI_NODE_NIL;
char *curr;
if (anynode == DI_NODE_NIL || alias == NULL)
return (NULL);
curr = alias_to_curr(anynode, alias, &currnode);
if (curr == NULL && currnode != DI_NODE_NIL) {
return (di_devfs_path(currnode));
} else if (curr == NULL) {
return (strdup(alias));
}
return (curr);
}
di_path_t
di_lookup_path(di_node_t root, char *devfspath)
{
di_node_t phci_node;
di_path_t path = DI_PATH_NIL;
char *copy, *lastslash;
char *pname, *paddr;
char *path_name, *path_addr;
if ((copy = strdup(devfspath)) == NULL) {
DPRINTF((DI_ERR, "strdup failed on: %s\n", devfspath));
return (DI_NODE_NIL);
}
if ((lastslash = strrchr(copy, '/')) == NULL) {
DPRINTF((DI_ERR, "failed to find component: %s\n", devfspath));
goto out;
}
*lastslash = '\0';
phci_node = di_lookup_node(root, copy);
if (phci_node == NULL) {
DPRINTF((DI_ERR, "failed to find component: %s\n", devfspath));
goto out;
}
pname = lastslash + 1;
if ((paddr = strchr(pname, '@')) == NULL) {
DPRINTF((DI_ERR, "failed to find unit-addr: %s\n", devfspath));
goto out;
}
*paddr++ = '\0';
for (path = di_path_phci_next_path(phci_node, DI_PATH_NIL);
path != DI_PATH_NIL;
path = di_path_phci_next_path(phci_node, path)) {
path_name = di_path_node_name(path);
path_addr = di_path_bus_addr(path);
if ((path_name == NULL) || (path_addr == NULL))
continue;
if ((strcmp(pname, path_name) == 0) &&
(strcmp(paddr, path_addr) == 0))
break;
}
out: free(copy);
return (path);
}
static char *
msglevel2str(di_debug_t msglevel)
{
switch (msglevel) {
case DI_ERR:
return ("ERROR");
case DI_INFO:
return ("Info");
case DI_TRACE:
return ("Trace");
case DI_TRACE1:
return ("Trace1");
case DI_TRACE2:
return ("Trace2");
default:
return ("UNKNOWN");
}
}
void
dprint(di_debug_t msglevel, const char *fmt, ...)
{
va_list ap;
char *estr;
if (di_debug <= DI_QUIET)
return;
if (di_debug < msglevel)
return;
estr = msglevel2str(msglevel);
assert(estr);
va_start(ap, fmt);
(void) fprintf(stderr, "libdevinfo[%lu]: %s: ",
(ulong_t)getpid(), estr);
(void) vfprintf(stderr, fmt, ap);
va_end(ap);
}