diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libdevinfo/devinfo.c | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libdevinfo/devinfo.c')
-rw-r--r-- | usr/src/lib/libdevinfo/devinfo.c | 3080 |
1 files changed, 3080 insertions, 0 deletions
diff --git a/usr/src/lib/libdevinfo/devinfo.c b/usr/src/lib/libdevinfo/devinfo.c new file mode 100644 index 0000000000..7da077cfba --- /dev/null +++ b/usr/src/lib/libdevinfo/devinfo.c @@ -0,0 +1,3080 @@ +/* + * 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" + +/* + * Interfaces for getting device configuration data from kernel + * through the devinfo driver. + */ + +#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> + +#define NDEBUG 1 +#include <assert.h> + +#include "libdevinfo.h" + +/* + * Debug message levels + */ +typedef enum { + DI_QUIET = 0, /* No debug messages - the default */ + 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)); +} + +/* + * We use blocking_open() to guarantee access to the devinfo device, if open() + * is failing with EAGAIN. + */ +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); +} + +/* private interface */ +di_node_t +di_init_driver(const char *drv_name, uint_t flag) +{ + int fd; + char driver[MAXPATHLEN]; + + /* + * Don't allow drv_name to exceed MAXPATHLEN - 1, or 1023, + * which should be sufficient for any sensible programmer. + */ + if ((drv_name == NULL) || (strlen(drv_name) >= MAXPATHLEN)) { + errno = EINVAL; + return (DI_NODE_NIL); + } + (void) strcpy(driver, drv_name); + + /* + * open the devinfo driver + */ + 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); + + /* + * Driver load succeeded, return a snapshot + */ + 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")); + + /* + * Make sure there is no minor name in the path + * and the path do not start with /devices.... + */ + if (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 private data is requested, copy the format specification + */ + if (flag & DINFOPRIVDATA & 0xff) { + if (priv) + bcopy(priv, &dinfo_io.priv, + sizeof (struct di_priv_data)); + else { + errno = EINVAL; + return (DI_NODE_NIL); + } + } + + /* + * Attempt to open the devinfo driver. Make a second attempt at the + * read-only minor node if we don't have privileges to open the full + * version _and_ if we're not requesting operations that the read-only + * node can't perform. (Setgid processes would fail an access() test, + * of course.) + */ + if ((fd = blocking_open("/devices/pseudo/devinfo@0:devinfo", + O_RDONLY)) == -1) { + if ((flag & DINFOFORCE) == DINFOFORCE || + (flag & DINFOPRIVDATA) == DINFOPRIVDATA) { + /* + * We wanted to perform a privileged operation, but the + * privileged node isn't available. Don't modify errno + * on our way out (but display it if we're running with + * di_debug set). + */ + 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); + } + } + + /* + * Verify that there is no major conflict, i.e., we are indeed opening + * the devinfo driver. + */ + 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); + } + + /* + * create snapshot + */ + 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); + } + + /* + * copy snapshot to userland + */ + 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->top_devinfo == 0) { /* phys_path not found */ + 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; /* starting address of map */ + + DPRINTF((DI_INFO, "di_fini: freeing a snapshot\n")); + + /* + * paranoid checking + */ + if (root == DI_NODE_NIL) { + DPRINTF((DI_ERR, "di_fini called with NIL arg\n")); + return; + } + + /* + * The root contains its own offset--self. + * Subtracting it from root address, we get the starting addr. + * The map_size is stored at the beginning of snapshot. + * Once we have starting address and size, we can free(). + */ + pa = (caddr_t)root - DI_NODE(root)->self; + + free(pa); +} + +di_node_t +di_parent_node(di_node_t node) +{ + caddr_t pa; /* starting address of map */ + + 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)); + } + + /* + * Deal with error condition: + * If parent doesn't exist and node is not the root, + * set errno to ENOTSUP. Otherwise, set errno to ENXIO. + */ + 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; /* starting address of map */ + + 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)); + } + + /* + * Deal with error condition: + * Sibling doesn't exist, figure out if ioctl command + * has DINFOSUBTREE set. If it doesn't, set errno to + * ENOTSUP. + */ + 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; /* starting address of map */ + + 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)); + } + + /* + * Deal with error condition: + * Child doesn't exist, figure out if DINFOSUBTREE is set. + * If it isn't, set errno to ENOTSUP. + */ + 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; /* starting address of map */ + 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); + } + + /* + * get major number of driver + */ + 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; /* starting address of map */ + + 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 == NULL) { + errno = ENXIO; + return (DI_NODE_NIL); + } + + return (DI_NODE(pa + DI_NODE(node)->next)); +} + +/* + * Internal library interfaces: + * node_list etc. for node walking + */ +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) /* a minor optimization */ + 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) /* a minor optimization */ + return; + + while (list->next) + list = list->next; + + list->next = tmp; +} + +/* + * returns 1 if node is a descendant of parent, 0 otherwise + */ +static int +is_descendant(di_node_t node, di_node_t parent) +{ + /* + * DI_NODE_NIL is parent of root, so it is + * the parent of all nodes. + */ + 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); +} + +/* + * Insert list before the first node which is NOT a descendent of parent. + * This is needed to reproduce the exact walking order of link generators. + */ +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) { /* a minor optimization */ + *headp = list; + return; + } + + if (!is_descendant(tmp->node, parent)) { + prepend_node_list(headp, list); + return; + } + + /* + * Find first node which is not a descendant + */ + while (tmp->next && is_descendant(tmp->next->node, parent)) { + tmp = tmp->next; + } + + tmp1 = tmp->next; + tmp->next = list; + append_node_list(headp, tmp1); +} + +/* + * Get a linked list of handles of all children + */ +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); +} + +/* + * Internal library interface: + * Delete all siblings of the first node from the node_list, along with + * the first node itself. + */ +static void +prune_sib(struct node_list **headp) +{ + di_node_t parent, curr_par, curr_gpar; + struct node_list *curr, *prev; + + /* + * get handle to parent of first node + */ + if ((parent = di_parent_node((*headp)->node)) == DI_NODE_NIL) { + /* + * This must be the root of the snapshot, so can't + * have any siblings. + * + * XXX Put a check here just in case. + */ + if ((*headp)->next) + DPRINTF((DI_ERR, "Unexpected err in di_walk_node.\n")); + + free(*headp); + *headp = NULL; + return; + } + + /* + * To be complete, we should also delete the children + * of siblings that have already been visited. + * This happens for DI_WALK_SIBFIRST when the first node + * is NOT the first in the linked list of siblings. + * + * Hence, we compare parent with BOTH the parent and grandparent + * of nodes, and delete node is a match is found. + */ + 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))) { + /* + * match parent/grandparent: delete curr + */ + prev->next = curr->next; + free(curr); + curr = prev->next; + } else + curr = curr->next; + } + + /* + * delete the first node + */ + curr = *headp; + *headp = curr->next; + free(curr); +} + +/* + * Internal library function: + * Update node list based on action (return code from callback) + * and flag specifying walking behavior. + */ +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: + /* + * free the node list and be done + */ + children = NULL; + free_node_list(headp); + break; + + case DI_WALK_PRUNESIB: + /* + * Get list of children and prune siblings + */ + children = get_children((*headp)->node); + prune_sib(headp); + break; + + case DI_WALK_PRUNECHILD: + /* + * Set children to NULL and pop first node + */ + children = NULL; + tmp = *headp; + *headp = tmp->next; + free(tmp); + break; + + case DI_WALK_CONTINUE: + default: + /* + * Get list of children and pop first node + */ + children = get_children((*headp)->node); + tmp = *headp; + *headp = tmp->next; + free(tmp); + break; + } + + /* + * insert the list of children + */ + 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; + } +} + +/* + * Internal library function: + * Invoke callback on one node and update the list of nodes to be walked + * based on the flag and return code. + */ +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; /* node_list for tree walk */ + + 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); +} + +/* + * Internal library function: + * Invoke callback for each minor on the minor list of first node + * on node_list headp, and place children of first node on the list. + * + * This is similar to walk_one_node, except we only walk in child + * first mode. + */ +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; /* node_list for tree walk */ + +#ifdef DEBUG + char *path = di_devfs_path(root); + DPRINTF((DI_INFO, "walking minor nodes under %s\n", path)); + di_devfs_path_free(path); +#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); +} + +/* + * generic node parameters + * Calling these routines always succeeds. + */ +char * +di_node_name(di_node_t node) +{ + return ((caddr_t)node + DI_NODE(node)->node_name - DI_NODE(node)->self); +} + +/* returns NULL ptr or a valid ptr to non-NULL string */ +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); +} + +/* + * XXX: emulate the return value of the old implementation + * using info from devi_node_class and devi_node_attributes. + */ +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_OFFLINE; + 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); +} + +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); +} + +/* + * returns the length of the path, caller must free memory + */ +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); + } + + /* + * trace back to root, note the node_name & address + */ + while ((parent = di_parent_node(node)) != DI_NODE_NIL) { + name[depth] = di_node_name(node); + len += strlen(name[depth]) + 1; /* 1 for '/' */ + + if ((addr[depth] = di_bus_addr(node)) != NULL) + len += strlen(addr[depth]) + 1; /* 1 for '@' */ + + node = parent; + depth++; + } + + /* + * get the path to the root of snapshot + */ + pa = (caddr_t)node - DI_NODE(node)->self; + name[depth] = DI_ALL(pa)->root_path; + len += strlen(name[depth]) + 1; + + /* + * allocate buffer and assemble path + */ + if ((buf = malloc(len)) == NULL) { + return (NULL); + } + + (void) strcpy(buf, name[depth]); + len = strlen(buf); + if (buf[len - 1] == '/') + len--; /* delete trailing '/' */ + + 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, *path; + int full_path_len; + + if (minor == DI_MINOR_NIL) { + errno = EINVAL; + return (NULL); + } + + name = di_minor_name(minor); + node = di_minor_devinfo(minor); + path = di_devfs_path(node); + if (path == NULL) + return (NULL); + + /* make the full path to the device minor node */ + full_path_len = strlen(path) + strlen(name) + 2; + full_path = (char *)calloc(1, full_path_len); + if (full_path != NULL) + (void) snprintf(full_path, full_path_len, "%s:%s", path, name); + + di_devfs_path_free(path); + return (full_path); +} + +void +di_devfs_path_free(char *buf) +{ + if (buf == NULL) { + DPRINTF((DI_ERR, "di_devfs_path_free NULL arg!\n")); + return; + } + + free(buf); +} + +/* minor data access */ +di_minor_t +di_minor_next(di_node_t node, di_minor_t minor) +{ + caddr_t pa; + + /* + * paranoid error checking + */ + if (node == DI_NODE_NIL) { + errno = EINVAL; + return (DI_MINOR_NIL); + } + + /* + * minor is not 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); + } + } + + /* + * minor is NIL-->caller asks for first minor node + */ + if (DI_NODE(node)->minor_data != 0) { + return (DI_MINOR((caddr_t)node - DI_NODE(node)->self + + DI_NODE(node)->minor_data)); + } + + /* + * no minor data-->check if snapshot includes minor data + * in order to set the correct errno + */ + pa = (caddr_t)node - DI_NODE(node)->self; + if (DINFOMINOR & DI_ALL(pa)->command) + errno = ENXIO; + else + errno = ENOTSUP; + + return (DI_MINOR_NIL); +} + +/* private interface for dealing with alias minor link generation */ +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); +} + +/* + * Single public interface for accessing software properties + */ +di_prop_t +di_prop_next(di_node_t node, di_prop_t prop) +{ + int list = DI_PROP_DRV_LIST; + + /* + * paranoid check + */ + if (node == DI_NODE_NIL) { + errno = EINVAL; + return (DI_PROP_NIL); + } + + /* + * Find which prop list we are at + */ + 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: /* shouldn't happen */ + 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); + + /* + * Shouldn't get here. In case we do, return unknown type. + * + * XXX--When DDI_PROP_TYPE_COMPOSITE is implemented, we need + * to add DI_PROP_TYPE_COMPOSITE. + */ + DPRINTF((DI_ERR, "Unimplemented property type: 0x%x\n", flags)); + + return (DI_PROP_TYPE_UNKNOWN); +} + +/* + * Extract type-specific values of an property + */ +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); /* boolean property */ + + 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); /* boolean property */ + + 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); /* boolean property */ + + 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); /* boolean property */ + + 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)); +} + +/* + * returns 1 for match, 0 for no match + */ +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 /* DEBUG */ + + 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); + + /* + * XXX prop_type is different from DDI_*. See PSARC 1997/127. + */ + 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; + + /* + * The check on match_dev follows ddi_prop_lookup_common(). + * Other checks are libdevinfo specific implementation. + */ + 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); +} + +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)); +} + +/* + * Consolidation private property access functions + */ +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); + } + } + + + /* + * prop is NIL, caller asks for first property + */ + 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) { + /* no global property */ + prop_off = 0; + } else if ((prop_off == 0) && (DI_NODE(node)->drv_major >= 0)) { + /* refer to devnames array */ + 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)); + } + + /* + * no prop found. Check the reason for not found + */ + 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 /* DEBUG */ + + 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); + } + + /* + * No memory allocation. + */ + *prop_data = (uchar_t *)((caddr_t)prop - DI_PROP(prop)->self + + DI_PROP(prop)->prop_data); + + return (DI_PROP(prop)->prop_len); +} + +/* + * Consolidation private interfaces for accessing I/O multipathing data + */ +di_path_t +di_path_next_client(di_node_t node, di_path_t path) +{ + caddr_t pa; + + /* + * path is not NIL + */ + 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); + } + } + + /* + * Path is NIL; the caller is asking for the first path info node + */ + if (DI_NODE(node)->multipath_phci != 0) { + DPRINTF((DI_INFO, "phci: 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)); + } + + /* + * No pathing data; check if the snapshot includes path data in order + * to set errno properly. + */ + 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_next_phci(di_node_t node, di_path_t path) +{ + caddr_t pa; + + /* + * path is not NIL + */ + 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); + } + } + + /* + * Path is NIL; the caller is asking for the first path info node + */ + if (DI_NODE(node)->multipath_client != 0) { + DPRINTF((DI_INFO, "client: 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)); + } + + /* + * No pathing data; check if the snapshot includes path data in order + * to set errno properly. + */ + pa = (caddr_t)node - DI_NODE(node)->self; + if (DINFOPATH & (DI_ALL(pa)->command)) + errno = ENXIO; + else + errno = ENOTSUP; + + return (DI_PATH_NIL); +} + +/* + * XXX Obsolete wrapper to be removed. Won't work under multilevel. + */ +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_next_phci(node, path)); + } else if (DI_NODE(node)->multipath_phci) { + return (di_path_next_client(node, path)); + } else { + /* + * The node had multipathing data but didn't appear to be a + * phci *or* a client; probably a programmer error. + */ + errno = EINVAL; + return (DI_PATH_NIL); + } +} + +di_path_state_t +di_path_state(di_path_t path) +{ + return ((di_path_state_t)DI_PATH(path)->path_state); +} + +char * +di_path_addr(di_path_t path, char *buf) +{ + caddr_t pa; /* starting address of map */ + + pa = (caddr_t)path - DI_PATH(path)->self; + + (void) strncpy(buf, (char *)(pa + DI_PATH(path)->path_addr), + MAXPATHLEN); + return (buf); +} + +di_node_t +di_path_client_node(di_path_t path) +{ + caddr_t pa; /* starting address of map */ + + 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)); + } + + /* + * Deal with error condition: + * If parent doesn't exist and node is not the root, + * set errno to ENOTSUP. Otherwise, set errno to ENXIO. + */ + 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; /* starting address of map */ + + 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)); + } + + /* + * Deal with error condition: + * If parent doesn't exist and node is not the root, + * set errno to ENOTSUP. Otherwise, set errno to ENXIO. + */ + 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); + } + + /* + * prop is not 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); + } + } + + /* + * prop is NIL-->caller asks for first property + */ + pa = (caddr_t)path - DI_PATH(path)->self; + if (DI_PATH(path)->path_prop != 0) { + return (DI_PATHPROP(pa + DI_PATH(path)->path_prop)); + } + + /* + * no property data-->check if snapshot includes props + * in order to set the correct errno + */ + 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; /* starting address of map */ + 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; + + /* + * Sanity check arguments + */ + 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)); +} + + +/* + * Consolidation private interfaces for private data + */ +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) { + /* + * Private data requested, but not obtained due to a memory + * error (e.g. wrong format specified) + */ + 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) { + /* + * Private data requested, but not obtained due to a memory + * error (e.g. wrong format specified) + */ + 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); +} + +/* + * PROM property access + */ + +/* + * openprom driver stuff: + * The maximum property length depends on the buffer size. We use + * OPROMMAXPARAM defined in <sys/openpromio.h> + * + * MAXNAMESZ is max property name. obpdefs.h defines it as 32 based on 1275 + * MAXVALSZ is maximum value size, which is whatever space left in buf + */ + +#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; /* form a linked list */ +}; + +struct di_prom_handle { /* handle to prom */ + mutex_t lock; /* synchronize access to openprom fd */ + int fd; /* /dev/openprom file descriptor */ + struct di_prom_prop *list; /* linked list of prop */ + 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); +} + +/* + * Internal library interface for locating the property + * XXX: ph->lock must be held for the duration of call. + */ +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)); + + /* + * Set "current" nodeid in the openprom driver + */ + 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); + + /* + * Prom property found. Allocate struct for storing prop + * (reuse variable prop) + */ + if ((prop = malloc(sizeof (struct di_prom_prop))) == NULL) + return (DI_PROM_PROP_NIL); + + /* + * Get a copy of property name + */ + if ((prop->name = strdup(opp->oprom_array)) == NULL) { + free(prop); + return (DI_PROM_PROP_NIL); + } + + /* + * get property value and length + */ + 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); + } + + /* + * make a copy of the property value + */ + 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); + + /* + * Prepend prop to list in prom handle + */ + 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)); + + /* + * paranoid check + */ + 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); + } + + /* + * synchronize access to prom file descriptor + */ + (void) mutex_lock(&p->lock); + + /* + * look for next property + */ + 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) +{ + /* + * paranoid check + */ + 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) +{ + /* + * paranoid check + */ + if (prom_prop == DI_PROM_PROP_NIL) { + errno = EINVAL; + return (NULL); + } + + *prom_prop_data = ((struct di_prom_prop *)prom_prop)->data; + + return (((struct di_prom_prop *)prom_prop)->len); +} + +/* + * Internal library interface for locating the property + * Returns length if found, -1 if prop doesn't exist. + */ +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; + + /* + * paranoid check + */ + 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); + } + + /* + * get property length + */ + 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)) { + /* no such property */ + (void) mutex_unlock(&p->lock); + return (NULL); + } + + /* + * Prom property found. Allocate struct for storing prop + */ + if ((prop = malloc(sizeof (struct di_prom_prop))) == NULL) { + (void) mutex_unlock(&p->lock); + return (NULL); + } + prop->name = NULL; /* we don't need the name */ + prop->len = opp->oprom_len; + + if (prop->len == 0) { /* boolean property */ + prop->data = NULL; + prop->next = p->list; + p->list = prop; + (void) mutex_unlock(&p->lock); + return (prop); + } + + /* + * retrieve the property value + */ + 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)) { + /* error retrieving property value */ + (void) mutex_unlock(&p->lock); + free(prop); + return (NULL); + } + + /* + * make a copy of the property value, stick in ph->list + */ + 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) { /* boolean property */ + *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) { /* boolean property */ + *prom_prop_data = NULL; + return (0); + } + + /* + * Fix an openprom bug (OBP string not NULL terminated). + * XXX This should really be fixed in promif. + */ + 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) { /* boolean property */ + *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); +} + +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)); + } + /* NOTREACHED */ +} + +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 *)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 *)DI_NODE(node)->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 *)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 *)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; + + /* + * paranoid error checking + */ + 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 != NULL) + return (DI_LNODE((caddr_t)di_all + + DI_NODE(node)->lnodes)); + } else { + if (DI_LNODE(lnode)->node_next != NULL) + 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; + + /* + * paranoid error checking + */ + 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 != NULL) + return (DI_LINK((caddr_t)di_all + + DI_NODE(node)->src_links)); + } else { + if (DI_LINK(link)->src_node_next != NULL) + 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 != NULL) + return (DI_LINK((caddr_t)di_all + + DI_NODE(node)->tgt_links)); + } else { + if (DI_LINK(link)->tgt_node_next != NULL) + 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; + + /* + * paranoid error checking + */ + 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 == NULL) + return (DI_LINK_NIL); + return (DI_LINK((caddr_t)di_all + + DI_LNODE(lnode)->link_out)); + } else { + if (DI_LINK(link)->src_link_next == NULL) + 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 == NULL) + return (DI_LINK_NIL); + return (DI_LINK((caddr_t)di_all + + DI_LNODE(lnode)->link_in)); + } else { + if (DI_LINK(link)->tgt_link_next == NULL) + return (DI_LINK_NIL); + return (DI_LINK((caddr_t)di_all + + DI_LINK(link)->tgt_link_next)); + } + } + /* NOTREACHED */ +} + +/* + * Internal library function: + * Invoke callback for each link data on the link list of first node + * on node_list headp, and place children of first node on the list. + * + * This is similar to walk_one_node, except we only walk in child + * first mode. + */ +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; /* node_list for tree walk */ + +#ifdef DEBUG + char *path = di_devfs_path(root); + DPRINTF((DI_INFO, "walking %s link data under %s\n", + (endpoint == DI_LINK_SRC) ? "src" : "tgt", path)); + di_devfs_path_free(path); +#endif + + /* + * paranoid error checking + */ + 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); +} + +/* + * Internal library function: + * Invoke callback for each link data on the link list of first node + * on node_list headp, and place children of first node on the list. + * + * This is similar to walk_one_node, except we only walk in child + * first mode. + */ +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; /* node_list for tree walk */ + +#ifdef DEBUG + char *path = di_devfs_path(root); + DPRINTF((DI_INFO, "walking lnode data under %s\n", path)); + di_devfs_path_free(path); +#endif + + /* + * paranoid error checking + */ + 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); +} + +di_node_t +di_lookup_node(di_node_t root, char *path) +{ + struct di_all *dap; + di_node_t node; + char copy[MAXPATHLEN]; + char *slash, *pname, *paddr; + + /* + * Path must be absolute and musn't have duplicate slashes + */ + if (*path != '/' || strstr(path, "//")) { + DPRINTF((DI_ERR, "Invalid path: %s\n", path)); + 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 (strlcpy(copy, path, sizeof (copy)) >= sizeof (copy)) { + DPRINTF((DI_ERR, "path too long: %s\n", path)); + return (DI_NODE_NIL); + } + + for (slash = copy, node = root; slash; ) { + + /* + * Handle path = "/" case as well as trailing '/' + */ + if (*(slash + 1) == '\0') + break; + + /* + * More path-components exist. Deal with the next one + */ + 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; + + /* + * Mappings between a "path-address" and bus-addr + * + * paddr baddr + * --------------------- + * NULL NULL + * NULL "" + * "" N/A (invalid paddr) + */ + if (paddr && baddr && strcmp(paddr, baddr) == 0) + break; + if (paddr == NULL && (baddr == NULL || *baddr == '\0')) + break; + } + + /* + * No nodes in the sibling list or there was no match + */ + if (node == DI_NODE_NIL) { + DPRINTF((DI_ERR, "%s@%s: no node\n", pname, paddr)); + return (DI_NODE_NIL); + } + } + + assert(node != DI_NODE_NIL); + return (node); +} + +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); +} + +/* end of devinfo.c */ |