The devinfo(3) library provides userspace access to the internal device
authorHasso Tepper <hasso@dragonflybsd.org>
Tue, 30 Sep 2008 12:20:29 +0000 (12:20 +0000)
committerHasso Tepper <hasso@dragonflybsd.org>
Tue, 30 Sep 2008 12:20:29 +0000 (12:20 +0000)
hierarchy. The devinfo(8) utility can be used to view that information.
Ported by Sascha Wildner.

Obtained-from: FreeBSD

18 files changed:
gnu/usr.bin/groff/tmac/mdoc.local
lib/Makefile
lib/libdevinfo/Makefile [new file with mode: 0644]
lib/libdevinfo/devinfo.3 [new file with mode: 0644]
lib/libdevinfo/devinfo.c [new file with mode: 0644]
lib/libdevinfo/devinfo.h [new file with mode: 0644]
lib/libdevinfo/devinfo_var.h [new file with mode: 0644]
sys/kern/subr_bus.c
sys/kern/subr_rman.c
sys/sys/bus.h
sys/sys/bus_private.h
sys/sys/rman.h
sys/sys/sysctl.h
usr.sbin/Makefile
usr.sbin/devinfo/Makefile [new file with mode: 0644]
usr.sbin/devinfo/devinfo.8 [new file with mode: 0644]
usr.sbin/devinfo/devinfo.c [new file with mode: 0644]
usr.sbin/pciconf/pciconf.8

index 9e95f56..424fa90 100644 (file)
@@ -23,7 +23,7 @@
 .\" SUCH DAMAGE.
 .\"
 .\" $FreeBSD: src/gnu/usr.bin/groff/tmac/mdoc.local,v 1.2.2.19 2003/03/25 10:20:12 murray Exp $
-.\" $DragonFly: src/gnu/usr.bin/groff/tmac/mdoc.local,v 1.25 2008/09/14 20:03:06 swildner Exp $
+.\" $DragonFly: src/gnu/usr.bin/groff/tmac/mdoc.local,v 1.26 2008/09/30 12:20:29 hasso Exp $
 .\"
 .\"     %beginstrip%
 .
@@ -45,6 +45,7 @@
 .ds doc-str-Lb-libcam           Common Access Method User Library (libcam, \-lcam)
 .ds doc-str-Lb-libcaps          CAPS IPC Library (libcaps, \-lcaps)
 .ds doc-str-Lb-libcipher        FreeSec Crypt Library (libcipher, \-lcipher)
+.ds doc-str-Lb-libdevinfo      Device and Resource Information Utility Library (libdevinfo, \-ldevinfo)
 .ds doc-str-Lb-libdevstat       Device Statistics Library (libdevstat, \-ldevstat)
 .ds doc-str-Lb-libfetch         File Transfer Library (libfetch, \-lfetch)
 .ds doc-str-Lb-libftpio         FTP Connection Management Library (libftpio, \-lftpio)
index 632533d..eacd8e1 100644 (file)
@@ -1,6 +1,6 @@
 #      @(#)Makefile    8.1 (Berkeley) 6/4/93
 # $FreeBSD: src/lib/Makefile,v 1.107.2.16 2002/10/10 19:24:35 kbyanc Exp $
-# $DragonFly: src/lib/Makefile,v 1.34 2008/07/23 14:09:05 swildner Exp $
+# $DragonFly: src/lib/Makefile,v 1.35 2008/09/30 12:20:29 hasso Exp $
 
 # To satisfy shared library or ELF linkage when only the libraries being
 # built are visible:
@@ -23,7 +23,7 @@
 SUBDIR=        libarchive libbluetooth libcom_err libcrypt libm libmd \
        libncurses libradius libskey libtacplus libutil libsbuf \
        libalias libatm ${_libbind} ${_libbind9} libbz2 libc ${_libc_r} \
-       libcalendar libcam libcompat libdevstat libedit libevent libfetch \
+       libcalendar libcam libcompat libdevinfo libdevstat libedit libevent libfetch \
        libftpio libipsec libipx libisc libkcore libkinfo libkvm libmagic \
        ${_libmilter} ${_libncp} libnetgraph libopie libpam \
        libpcap libposix1e libsdp libthread_xu libpthread librpcsvc ${_libsm} \
diff --git a/lib/libdevinfo/Makefile b/lib/libdevinfo/Makefile
new file mode 100644 (file)
index 0000000..607ab11
--- /dev/null
@@ -0,0 +1,9 @@
+# $FreeBSD: src/lib/libdevinfo/Makefile,v 1.4 2002/05/13 12:11:54 ru Exp $
+# $DragonFly: src/lib/libdevinfo/Makefile,v 1.1 2008/09/30 12:20:29 hasso Exp $
+
+LIB=   devinfo
+SRCS=  devinfo.c
+INCS=  devinfo.h
+MAN=   devinfo.3
+
+.include <bsd.lib.mk>
diff --git a/lib/libdevinfo/devinfo.3 b/lib/libdevinfo/devinfo.3
new file mode 100644 (file)
index 0000000..99b45ab
--- /dev/null
@@ -0,0 +1,250 @@
+.\"
+.\" Copyright (c) 2001 Michael Smith <msmith@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD: src/lib/libdevinfo/devinfo.3,v 1.11 2006/09/17 21:27:35 ru Exp $
+.\" $DragonFly: src/lib/libdevinfo/devinfo.3,v 1.1 2008/09/30 12:20:29 hasso Exp $
+.\"
+.Dd September 30, 2008
+.Dt DEVINFO 3
+.Os
+.Sh NAME
+.Nm devinfo ,
+.Nm devinfo_init ,
+.Nm devinfo_free ,
+.Nm devinfo_handle_to_device ,
+.Nm devinfo_handle_to_resource ,
+.Nm devinfo_handle_to_rman ,
+.Nm devinfo_foreach_device_child ,
+.Nm devinfo_foreach_device_resource ,
+.Nm devinfo_foreach_rman_resource ,
+.Nm devinfo_foreach_rman
+.Nd device and resource information utility library
+.Sh LIBRARY
+.Lb libdevinfo
+.Sh SYNOPSIS
+.In devinfo.h
+.Ft int
+.Fn devinfo_init "void"
+.Ft void
+.Fn devinfo_free "void"
+.Ft struct devinfo_dev *
+.Fn devinfo_handle_to_device "devinfo_handle_t handle"
+.Ft struct devinfo_res *
+.Fn devinfo_handle_to_resource "devinfo_handle_t handle"
+.Ft struct devinfo_rman *
+.Fn devinfo_handle_to_rman "devinfo_handle_t handle"
+.Ft int
+.Fo devinfo_foreach_device_child
+.Fa "struct devinfo_dev *parent"
+.Fa "int \*[lp]*fn\*[rp]\*[lp]struct devinfo_dev *child, void *arg\*[rp]"
+.Fa "void *arg"
+.Fc
+.Ft int
+.Fo devinfo_foreach_device_resource
+.Fa "struct devinfo_dev *dev"
+.Fa "int \*[lp]*fn\*[rp]\*[lp]struct devinfo_dev *dev, \:struct devinfo_res *res, void *arg\*[rp]"
+.Fa "void *arg"
+.Fc
+.Ft int
+.Fo devinfo_foreach_rman_resource
+.Fa "struct devinfo_rman *rman"
+.Fa "int \*[lp]*fn\*[rp]\*[lp]struct devinfo_res *res, void *arg\*[rp]"
+.Fa "void *arg"
+.Fc
+.Ft int
+.Fo devinfo_foreach_rman
+.Fa "int \*[lp]*fn\*[rp]\*[lp]struct devinfo_rman *rman, void *arg\*[rp]"
+.Fa "void *arg"
+.Fc
+.Sh DESCRIPTION
+The
+.Nm
+library provides access to the kernel's internal device hierarchy
+and to the I/O resource manager.
+The library uses a
+.Xr sysctl 3
+interface to obtain a snapshot of the kernel's state,
+which is then made available to the application.
+.Pp
+Due to the fact that the information may be logically arranged
+in a number of different fashions,
+the library does not attempt to impose any structure on the data.
+.Pp
+Device, resource, and resource manager information is returned in
+data structures defined in
+.In devinfo.h :
+.Bd -literal -offset indent
+struct devinfo_dev {
+    devinfo_handle_t   dd_handle;      /* device handle */
+    devinfo_handle_t   dd_parent;      /* parent handle */
+    char               *dd_name;       /* name of device */
+    char               *dd_desc;       /* device description */
+    char               *dd_drivername; /* name of attached driver */
+    char               *dd_pnpinfo;    /* pnp info from parent bus */
+    char               *dd_location;   /* Where bus thinks dev at */
+    uint32_t           dd_devflags;    /* API flags */
+    uint16_t           dd_flags;       /* internal dev flags */
+    device_state_t     dd_state;       /* attachment state of dev */
+};
+
+struct devinfo_rman {
+    devinfo_handle_t   dm_handle;      /* resource manager handle */
+    u_long             dm_start;       /* resource start */
+    u_long             dm_size;        /* resource size */
+    char               *dm_desc;       /* resource description */
+};
+
+struct devinfo_res {
+    devinfo_handle_t   dr_handle;      /* resource handle */
+    devinfo_handle_t   dr_rman;        /* resource manager handle */
+    devinfo_handle_t   dr_device;      /* owning device */
+    u_long             dr_start;       /* region start */
+    u_long             dr_size;        /* region size */
+};
+.Ed
+.Pp
+The
+.Vt devinfo_handle_t
+values can be used to look up the correspondingly referenced structures.
+.Pp
+.Fn devinfo_init
+takes a snapshot of the kernel's internal device and resource state.
+It returns nonzero
+if after a number of retries a consistent snapshot cannot be obtained.
+.Fn devinfo_init
+must be called before any other functions can be used.
+.Pp
+.Fn devinfo_free
+releases the memory associated with the snapshot.
+Any pointers returned by other functions are invalidated by this,
+and
+.Fn devinfo_init
+must be called again before using any other functions.
+.Pp
+.Fn devinfo_handle_to_device ,
+.Fn devinfo_handle_to_resource
+and
+.Fn devinfo_handle_to_rman
+return pointers to
+.Vt devinfo_dev ,
+.Vt devinfo_res
+and
+.Vt devinfo_rman
+structures respectively based on the
+.Vt devinfo_handle_t
+passed to them.
+These functions can be used to traverse the tree from any node to any
+other node.
+If
+.Fn devinfo_handle_to_device
+is passed the constant
+.Dv DEVINFO_ROOT_DEVICE
+it will return the handle to the root of the device tree.
+.Pp
+.Fn devinfo_foreach_device_child
+invokes its callback argument
+.Fa fn
+on every device which is an immediate child of
+.Fa device .
+The
+.Fa fn
+function is also passed
+.Fa arg ,
+allowing state to be passed to the callback function.
+If
+.Fa fn
+returns a nonzero error value the traversal is halted,
+and
+.Fn devinfo_foreach_device_child
+returns the error value to its caller.
+.Pp
+.Fn devinfo_foreach_device_resource
+invokes its callback argument
+.Fa fn
+on every resource which is owned by
+.Fa device .
+The
+.Fa fn
+function is also passed
+.Fa device
+and
+.Fa arg ,
+allowing state to be passed to the callback function.
+If
+.Fa fn
+returns a nonzero error value the traversal is halted,
+and
+.Fn devinfo_foreach_device_resource
+returns the error value to its caller.
+.Pp
+.Fn devinfo_foreach_rman_resource
+invokes its callback argument
+.Fa fn
+on every resource within the resource manager
+.Fa rman .
+The
+.Fa fn
+function is also passed
+.Fa arg ,
+allowing state to be passed to the callback function.
+If
+.Fa fn
+returns a nonzero error value the traversal is halted,
+and
+.Fn devinfo_foreach_rman_resource
+returns the error value to its caller.
+.Pp
+.Fn devinfo_foreach_rman
+invokes its callback argument
+.Fa fn
+on every resource manager.
+The
+.Fa fn
+function is also passed
+.Fa arg ,
+allowing state to be passed to the callback function.
+If
+.Fa fn
+returns a nonzero error value the traversal is halted,
+and
+.Fn devinfo_foreach_rman
+returns the error value to its caller.
+.Sh SEE ALSO
+.Xr devstat 3
+.Sh HISTORY
+The
+.Nm
+library first appeared in
+.Fx 5.0
+and was imported into
+.Dx 1.11 .
+.Sh AUTHORS
+.An Michael Smith Aq msmith@FreeBSD.org
+.Sh BUGS
+This is the first implementation of the library,
+and the interface is still subject to refinement.
+.Pp
+The interface does not report device classes or drivers,
+making it hard to sort by class or driver.
diff --git a/lib/libdevinfo/devinfo.c b/lib/libdevinfo/devinfo.c
new file mode 100644 (file)
index 0000000..ffcc2bb
--- /dev/null
@@ -0,0 +1,503 @@
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libdevinfo/devinfo.c,v 1.9 2006/07/17 09:33:24 stefanf Exp $
+ * $DragonFly: src/lib/libdevinfo/devinfo.c,v 1.1 2008/09/30 12:20:29 hasso Exp $
+ */
+
+/*
+ * An interface to the FreeBSD kernel's bus/device information interface.
+ *
+ * This interface is implemented with the
+ *
+ * hw.bus
+ * hw.bus.devices
+ * hw.bus.rman
+ * 
+ * sysctls.  The interface is not meant for general user application
+ * consumption.
+ *
+ * Device information is obtained by scanning a linear list of all devices
+ * maintained by the kernel.  The actual device structure pointers are
+ * handed out as opaque handles in order to allow reconstruction of the
+ * logical toplogy in user space.
+ *
+ * Resource information is obtained by scanning the kernel's resource
+ * managers and fetching their contents.  Ownership of resources is
+ * tracked using the device's structure pointer again as a handle.
+ *
+ * In order to ensure coherency of the library's picture of the kernel,
+ * a generation count is maintained by the kernel.  The initial generation
+ * count is obtained (along with the interface version) from the hw.bus
+ * sysctl, and must be passed in with every request.  If the generation
+ * number supplied by the library does not match the kernel's current
+ * generation number, the request is failed and the library must discard
+ * the data it has received and rescan.
+ *
+ * The information obtained from the kernel is exported to consumers of
+ * this library through a variety of interfaces.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "devinfo.h"
+#include "devinfo_var.h"
+
+static int     devinfo_init_devices(int generation);
+static int     devinfo_init_resources(int generation);
+
+TAILQ_HEAD(,devinfo_i_dev)     devinfo_dev;
+TAILQ_HEAD(,devinfo_i_rman)    devinfo_rman;
+TAILQ_HEAD(,devinfo_i_res)     devinfo_res;
+
+static int     devinfo_initted = 0;
+static int     devinfo_generation = 0;
+
+#if 0
+# define debug(...)    do { \
+       fprintf(stderr, "%s:", __func__); \
+       fprintf(stderr, __VA_ARGS__); \
+       fprintf(stderr, "\n"); \
+} while (0)
+#else
+# define debug(...)
+#endif
+
+/*
+ * Initialise our local database with the contents of the kernel's
+ * tables.
+ */
+int
+devinfo_init(void)
+{
+       struct u_businfo        ubus;
+       size_t          ub_size;
+       int                     error, retries;
+
+       if (!devinfo_initted) {
+               TAILQ_INIT(&devinfo_dev);
+               TAILQ_INIT(&devinfo_rman);
+               TAILQ_INIT(&devinfo_res);
+       }
+
+       /*
+        * Get the generation count and interface version, verify that we 
+        * are compatible with the kernel.
+        */
+       for (retries = 0; retries < 10; retries++) {
+               debug("get interface version");
+               ub_size = sizeof(ubus);
+               if (sysctlbyname("hw.bus.info", &ubus,
+                   &ub_size, NULL, 0) != 0) {
+                       warn("sysctlbyname(\"hw.bus.info\", ...) failed");
+                       return(EINVAL);
+               }
+               if ((ub_size != sizeof(ubus)) ||
+                   (ubus.ub_version != BUS_USER_VERSION)) {
+                       warn("kernel bus interface version mismatch");
+                       return(EINVAL);
+               }
+               debug("generation count is %d", ubus.ub_generation);
+
+               /*
+                * Don't rescan if the generation count hasn't changed.
+                */
+               if (ubus.ub_generation == devinfo_generation)
+                       return(0);
+
+               /*
+                * Generation count changed, rescan
+                */
+               devinfo_free();
+               devinfo_initted = 0;
+               devinfo_generation = 0;
+
+               if ((error = devinfo_init_devices(ubus.ub_generation)) != 0) {
+                       devinfo_free();
+                       if (error == EINVAL)
+                               continue;
+                       break;
+               }
+               if ((error = devinfo_init_resources(ubus.ub_generation)) != 0) {
+                       devinfo_free();
+                       if (error == EINVAL)
+                               continue;
+                       break;
+               }
+               devinfo_initted = 1;
+               devinfo_generation = ubus.ub_generation;
+               return(0);
+       }
+       debug("scan failed after %d retries", retries);
+       errno = error;
+       return(1);
+}
+
+static int
+devinfo_init_devices(int generation)
+{
+       struct u_device         udev;
+       struct devinfo_i_dev    *dd;
+       int                     dev_idx;
+       int                     dev_ptr;
+       int                     name2oid[2];
+       int                     oid[CTL_MAXNAME + 12];
+       size_t                  oidlen, rlen;
+       char                    *name;
+       int                     error;
+
+       /* 
+        * Find the OID for the rman interface node.
+        * This is just the usual evil, undocumented sysctl juju.
+        */
+       name2oid[0] = 0;
+       name2oid[1] = 3;
+       oidlen = sizeof(oid);
+       name = "hw.bus.devices";
+       error = sysctl(name2oid, 2, oid, &oidlen, name, strlen(name));
+       if (error < 0) {
+               warnx("can't find hw.bus.devices sysctl node");
+               return(ENOENT);
+       }
+       oidlen /= sizeof(int);
+       if (oidlen > CTL_MAXNAME) {
+               warnx("hw.bus.devices oid is too large");
+               return(EINVAL);
+       }
+       oid[oidlen++] = generation;
+       dev_ptr = oidlen++;
+
+       /*
+        * Scan devices.
+        *
+        * Stop after a fairly insane number to avoid death in the case
+        * of kernel corruption.
+        */
+       for (dev_idx = 0; dev_idx < 1000; dev_idx++) {
+
+               /*
+                * Get the device information.
+                */
+               oid[dev_ptr] = dev_idx;
+               rlen = sizeof(udev);
+               error = sysctl(oid, oidlen, &udev, &rlen, NULL, 0);
+               if (error < 0) {
+                       if (errno == ENOENT)    /* end of list */
+                               break;
+                       if (errno != EINVAL)    /* gen count skip, restart */
+                               warn("sysctl hw.bus.devices.%d", dev_idx);
+                       return(errno);
+               }
+               if ((dd = malloc(sizeof(*dd))) == NULL)
+                       return(ENOMEM);
+               dd->dd_dev.dd_handle = udev.dv_handle;
+               dd->dd_dev.dd_parent = udev.dv_parent;
+               snprintf(dd->dd_name, sizeof(dd->dd_name), "%s", udev.dv_name);
+               dd->dd_dev.dd_name = &dd->dd_name[0];
+               snprintf(dd->dd_desc, sizeof(dd->dd_desc), "%s", udev.dv_desc);
+               dd->dd_dev.dd_desc = &dd->dd_desc[0];
+               snprintf(dd->dd_drivername, sizeof(dd->dd_drivername), "%s",
+                   udev.dv_drivername);
+               dd->dd_dev.dd_drivername = &dd->dd_drivername[0];
+               snprintf(dd->dd_pnpinfo, sizeof(dd->dd_pnpinfo), "%s",
+                   udev.dv_pnpinfo);
+               dd->dd_dev.dd_pnpinfo = &dd->dd_pnpinfo[0];
+               snprintf(dd->dd_location, sizeof(dd->dd_location), "%s",
+                   udev.dv_location);
+               dd->dd_dev.dd_location = &dd->dd_location[0];
+               dd->dd_dev.dd_devflags = udev.dv_devflags;
+               dd->dd_dev.dd_flags = udev.dv_flags;
+               dd->dd_dev.dd_state = udev.dv_state;
+               TAILQ_INSERT_TAIL(&devinfo_dev, dd, dd_link);
+       }
+       debug("fetched %d devices", dev_idx);
+       return(0);
+}
+
+static int
+devinfo_init_resources(int generation)
+{
+       struct u_rman           urman;
+       struct devinfo_i_rman   *dm;
+       struct u_resource       ures;
+       struct devinfo_i_res    *dr;
+       int                     rman_idx, res_idx;
+       int                     rman_ptr, res_ptr;
+       int                     name2oid[2];
+       int                     oid[CTL_MAXNAME + 12];
+       size_t                  oidlen, rlen;
+       char                    *name;
+       int                     error;
+
+       /* 
+        * Find the OID for the rman interface node.
+        * This is just the usual evil, undocumented sysctl juju.
+        */
+       name2oid[0] = 0;
+       name2oid[1] = 3;
+       oidlen = sizeof(oid);
+       name = "hw.bus.rman";
+       error = sysctl(name2oid, 2, oid, &oidlen, name, strlen(name));
+       if (error < 0) {
+               warnx("can't find hw.bus.rman sysctl node");
+               return(ENOENT);
+       }
+       oidlen /= sizeof(int);
+       if (oidlen > CTL_MAXNAME) {
+               warnx("hw.bus.rman oid is too large");
+               return(EINVAL);
+       }
+       oid[oidlen++] = generation;
+       rman_ptr = oidlen++;
+       res_ptr = oidlen++;
+
+       /*
+        * Scan resource managers.
+        *
+        * Stop after a fairly insane number to avoid death in the case
+        * of kernel corruption.
+        */
+       for (rman_idx = 0; rman_idx < 255; rman_idx++) {
+
+               /*
+                * Get the resource manager information.
+                */
+               oid[rman_ptr] = rman_idx;
+               oid[res_ptr] = -1;
+               rlen = sizeof(urman);
+               error = sysctl(oid, oidlen, &urman, &rlen, NULL, 0);
+               if (error < 0) {
+                       if (errno == ENOENT)    /* end of list */
+                               break;
+                       if (errno != EINVAL)    /* gen count skip, restart */
+                               warn("sysctl hw.bus.rman.%d", rman_idx);
+                       return(errno);
+               }
+               if ((dm = malloc(sizeof(*dm))) == NULL)
+                       return(ENOMEM);
+               dm->dm_rman.dm_handle = urman.rm_handle;
+               dm->dm_rman.dm_start = urman.rm_start;
+               dm->dm_rman.dm_size = urman.rm_size;
+               snprintf(dm->dm_desc, DEVINFO_STRLEN, "%s", urman.rm_descr);
+               dm->dm_rman.dm_desc = &dm->dm_desc[0];
+               TAILQ_INSERT_TAIL(&devinfo_rman, dm, dm_link);
+
+               /*
+                * Scan resources on this resource manager.
+                *
+                * Stop after a fairly insane number to avoid death in the case
+                * of kernel corruption.
+                */
+               for (res_idx = 0; res_idx < 1000; res_idx++) {
+                       /* 
+                        * Get the resource information.
+                        */
+                       oid[res_ptr] = res_idx;
+                       rlen = sizeof(ures);
+                       error = sysctl(oid, oidlen, &ures, &rlen, NULL, 0);
+                       if (error < 0) {
+                               if (errno == ENOENT)    /* end of list */
+                                       break;
+                               if (errno != EINVAL)    /* gen count skip */
+                                       warn("sysctl hw.bus.rman.%d.%d",
+                                           rman_idx, res_idx);
+                               return(errno);
+                       }
+                       if ((dr = malloc(sizeof(*dr))) == NULL)
+                               return(ENOMEM);
+                       dr->dr_res.dr_handle = ures.r_handle;
+                       dr->dr_res.dr_rman = ures.r_parent;
+                       dr->dr_res.dr_device = ures.r_device;
+                       dr->dr_res.dr_start = ures.r_start;
+                       dr->dr_res.dr_size = ures.r_size;
+                       TAILQ_INSERT_TAIL(&devinfo_res, dr, dr_link);
+               }
+               debug("fetched %d resources", res_idx);
+       }
+       debug("scanned %d resource managers", rman_idx);
+       return(0);
+}
+
+/*
+ * Free the list contents.
+ */
+void
+devinfo_free(void)
+{
+       struct devinfo_i_dev    *dd;
+       struct devinfo_i_rman   *dm;
+       struct devinfo_i_res    *dr;
+
+       while ((dd = TAILQ_FIRST(&devinfo_dev)) != NULL) {
+               TAILQ_REMOVE(&devinfo_dev, dd, dd_link);
+               free(dd);
+       }
+       while ((dm = TAILQ_FIRST(&devinfo_rman)) != NULL) {
+               TAILQ_REMOVE(&devinfo_rman, dm, dm_link);
+               free(dm);
+       }
+       while ((dr = TAILQ_FIRST(&devinfo_res)) != NULL) {
+               TAILQ_REMOVE(&devinfo_res, dr, dr_link);
+               free(dr);
+       }
+       devinfo_initted = 0;
+       devinfo_generation = 0;
+}
+
+/*
+ * Find a device by its handle.
+ */
+struct devinfo_dev *
+devinfo_handle_to_device(devinfo_handle_t handle)
+{
+       struct devinfo_i_dev    *dd;
+
+       /*
+        * Find the root device, whose parent is NULL
+        */
+       if (handle == DEVINFO_ROOT_DEVICE) {
+               TAILQ_FOREACH(dd, &devinfo_dev, dd_link)
+                   if (dd->dd_dev.dd_parent == DEVINFO_ROOT_DEVICE)
+                           return(&dd->dd_dev);
+               return(NULL);
+       }
+
+       /*
+        * Scan for the device
+        */
+       TAILQ_FOREACH(dd, &devinfo_dev, dd_link)
+           if (dd->dd_dev.dd_handle == handle)
+                   return(&dd->dd_dev);
+       return(NULL);
+}
+
+/*
+ * Find a resource by its handle.
+ */
+struct devinfo_res *
+devinfo_handle_to_resource(devinfo_handle_t handle)
+{
+       struct devinfo_i_res    *dr;
+
+       TAILQ_FOREACH(dr, &devinfo_res, dr_link)
+           if (dr->dr_res.dr_handle == handle)
+                   return(&dr->dr_res);
+       return(NULL);
+}
+
+/*
+ * Find a resource manager by its handle.
+ */
+struct devinfo_rman *
+devinfo_handle_to_rman(devinfo_handle_t handle)
+{
+       struct devinfo_i_rman   *dm;
+
+       TAILQ_FOREACH(dm, &devinfo_rman, dm_link)
+           if (dm->dm_rman.dm_handle == handle)
+                   return(&dm->dm_rman);
+       return(NULL);
+}
+
+/*
+ * Iterate over the children of a device, calling (fn) on each.  If
+ * (fn) returns nonzero, abort the scan and return.
+ */
+int
+devinfo_foreach_device_child(struct devinfo_dev *parent, 
+    int (* fn)(struct devinfo_dev *child, void *arg), 
+    void *arg)
+{
+       struct devinfo_i_dev    *dd;
+       int                             error;
+
+       TAILQ_FOREACH(dd, &devinfo_dev, dd_link)
+           if (dd->dd_dev.dd_parent == parent->dd_handle)
+                   if ((error = fn(&dd->dd_dev, arg)) != 0)
+                           return(error);
+       return(0);
+}
+
+/*
+ * Iterate over all the resources owned by a device, calling (fn) on each.
+ * If (fn) returns nonzero, abort the scan and return.
+ */
+int
+devinfo_foreach_device_resource(struct devinfo_dev *dev,
+    int (* fn)(struct devinfo_dev *dev, struct devinfo_res *res, void *arg),
+    void *arg)
+{
+       struct devinfo_i_res    *dr;
+       int                             error;
+
+       TAILQ_FOREACH(dr, &devinfo_res, dr_link)
+           if (dr->dr_res.dr_device == dev->dd_handle)
+                   if ((error = fn(dev, &dr->dr_res, arg)) != 0)
+                           return(error);
+       return(0);
+}
+
+/*
+ * Iterate over all the resources owned by a resource manager, calling (fn)
+ * on each.  If (fn) returns nonzero, abort the scan and return.
+ */
+extern int
+devinfo_foreach_rman_resource(struct devinfo_rman *rman,
+    int (* fn)(struct devinfo_res *res, void *arg),
+    void *arg)
+{
+       struct devinfo_i_res    *dr;
+       int                             error;
+
+       TAILQ_FOREACH(dr, &devinfo_res, dr_link)
+           if (dr->dr_res.dr_rman == rman->dm_handle)
+                   if ((error = fn(&dr->dr_res, arg)) != 0)
+                           return(error);
+       return(0);
+}
+
+/*
+ * Iterate over all the resource managers, calling (fn) on each.  If (fn)
+ * returns nonzero, abort the scan and return.
+ */
+extern int
+devinfo_foreach_rman(int (* fn)(struct devinfo_rman *rman, void *arg),
+    void *arg)
+{
+    struct devinfo_i_rman      *dm;
+    int                                error;
+
+    TAILQ_FOREACH(dm, &devinfo_rman, dm_link)
+       if ((error = fn(&dm->dm_rman, arg)) != 0)
+           return(error);
+    return(0);
+}
diff --git a/lib/libdevinfo/devinfo.h b/lib/libdevinfo/devinfo.h
new file mode 100644 (file)
index 0000000..8caf544
--- /dev/null
@@ -0,0 +1,148 @@
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libdevinfo/devinfo.h,v 1.5 2005/08/31 14:57:39 rodrigc Exp $
+ * $DragonFly: src/lib/libdevinfo/devinfo.h,v 1.1 2008/09/30 12:20:29 hasso Exp $
+ */
+
+#ifndef _DEVINFO_H_INCLUDED
+#define _DEVINFO_H_INCLUDED
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+typedef __uintptr_t    devinfo_handle_t;
+#define DEVINFO_ROOT_DEVICE    ((devinfo_handle_t)0)
+
+/*
+ * State of the device.
+ */
+/* XXX not sure if I want a copy here, or expose sys/bus.h */
+typedef enum devinfo_state {
+       DIS_NOTPRESENT,                 /* not probed or probe failed */
+       DIS_ALIVE,                      /* probe succeeded */
+       DIS_ATTACHED,                   /* attach method called */
+       DIS_BUSY                        /* device is open */
+} devinfo_state_t;
+
+struct devinfo_dev {
+       devinfo_handle_t        dd_handle;      /* device handle */
+       devinfo_handle_t        dd_parent;      /* parent handle */
+
+       char                    *dd_name;       /* name of device */
+       char                    *dd_desc;       /* device description */
+       char                    *dd_drivername; /* name of attached driver*/
+       char                    *dd_pnpinfo;    /* pnp info from parent bus */
+       char                    *dd_location;   /* Where bus thinks dev at */
+       uint32_t                dd_devflags;    /* API flags */
+       uint16_t                dd_flags;       /* internal dev flags */
+       devinfo_state_t         dd_state;       /* attacement state of dev */
+};
+
+struct devinfo_rman {
+       devinfo_handle_t        dm_handle;      /* resource manager handle */
+
+       unsigned long           dm_start;       /* resource start */
+       unsigned long           dm_size;        /* resource size */
+    
+       char                    *dm_desc;       /* resource description */
+};
+
+struct devinfo_res {
+       devinfo_handle_t        dr_handle;      /* resource handle */
+       devinfo_handle_t        dr_rman;        /* resource manager handle */
+       devinfo_handle_t        dr_device;      /* owning device */
+
+       unsigned long           dr_start;       /* region start */
+       unsigned long           dr_size;        /* region size */
+       /* XXX add flags */
+};
+
+__BEGIN_DECLS
+
+/*
+ * Acquire a coherent copy of the kernel's device and resource tables.
+ * This must return success (zero) before any other interfaces will
+ * function.  Sets errno on failure.
+ */
+extern int     devinfo_init(void);
+
+/*
+ * Release the storage associated with the internal copy of the device
+ * and resource tables. devinfo_init must be called before any attempt
+ * is made to use any other interfaces.
+ */
+extern void    devinfo_free(void);
+
+/*
+ * Find a device/resource/resource manager by its handle.
+ */
+extern struct devinfo_dev
+       *devinfo_handle_to_device(devinfo_handle_t handle);
+extern struct devinfo_res
+       *devinfo_handle_to_resource(devinfo_handle_t handle);
+extern struct devinfo_rman
+       *devinfo_handle_to_rman(devinfo_handle_t handle);
+
+/*
+ * Iterate over the children of a device, calling (fn) on each.  If
+ * (fn) returns nonzero, abort the scan and return.
+ */
+extern int
+       devinfo_foreach_device_child(struct devinfo_dev *parent, 
+           int (* fn)(struct devinfo_dev *child, void *arg), 
+           void *arg);
+
+/*
+ * Iterate over all the resources owned by a device, calling (fn) on each.
+ * If (fn) returns nonzero, abort the scan and return.
+ */
+extern int
+       devinfo_foreach_device_resource(struct devinfo_dev *dev,
+           int (* fn)(struct devinfo_dev *dev, 
+           struct devinfo_res *res, void *arg),
+           void *arg);
+
+/*
+ * Iterate over all the resources owned by a resource manager, calling (fn)
+ * on each.  If (fn) returns nonzero, abort the scan and return.
+ */
+extern int
+       devinfo_foreach_rman_resource(struct devinfo_rman *rman,
+           int (* fn)(struct devinfo_res *res, void *arg),
+           void *arg);
+
+/*
+ * Iterate over all the resource managers, calling (fn) on each.  If (fn)
+ * returns nonzero, abort the scan and return.
+ */
+extern int
+       devinfo_foreach_rman(int (* fn)(struct devinfo_rman *rman, void *arg),
+           void *arg);
+
+__END_DECLS
+
+#endif /* ! _DEVINFO_H_INCLUDED */
diff --git a/lib/libdevinfo/devinfo_var.h b/lib/libdevinfo/devinfo_var.h
new file mode 100644 (file)
index 0000000..5189d12
--- /dev/null
@@ -0,0 +1,73 @@
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libdevinfo/devinfo_var.h,v 1.3 2003/02/17 18:56:00 imp Exp $
+ * $DragonFly: src/lib/libdevinfo/devinfo_var.h,v 1.1 2008/09/30 12:20:29 hasso Exp $
+ */
+
+#define _KERNEL_STRUCTURES
+
+#include <sys/rman.h>
+#include <sys/bus.h>
+
+/*
+ * This is defined by the version 1 interface.
+ */
+#define DEVINFO_STRLEN 32
+
+/*
+ * Devices.  
+ *
+ * Internal structure contains string buffers and list linkage;
+ */
+struct devinfo_i_dev {
+       struct devinfo_dev              dd_dev;
+       char                            dd_name[DEVINFO_STRLEN];
+       char                            dd_desc[DEVINFO_STRLEN];
+       char                            dd_drivername[DEVINFO_STRLEN];
+       char                            dd_pnpinfo[DEVINFO_STRLEN * 4];
+       char                            dd_location[DEVINFO_STRLEN * 4];
+       uint32_t                        dd_devflags;
+       uint16_t                        dd_flags;
+       device_state_t                  dd_state;
+       TAILQ_ENTRY(devinfo_i_dev)      dd_link;
+};
+
+/*
+ * Resources.
+ *
+ * Internal structures contain string buffers and list linkage;
+ */
+struct devinfo_i_rman {
+       struct devinfo_rman             dm_rman;
+       char                            dm_desc[32];
+       TAILQ_ENTRY(devinfo_i_rman)     dm_link;
+};
+
+struct devinfo_i_res {
+       struct devinfo_res              dr_res;
+       TAILQ_ENTRY(devinfo_i_res)      dr_link;
+};
index 0cfe755..cc30836 100644 (file)
@@ -24,7 +24,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/kern/subr_bus.c,v 1.54.2.9 2002/10/10 15:13:32 jhb Exp $
- * $DragonFly: src/sys/kern/subr_bus.c,v 1.44 2008/09/29 06:59:45 hasso Exp $
+ * $DragonFly: src/sys/kern/subr_bus.c,v 1.45 2008/09/30 12:20:29 hasso Exp $
  */
 
 #include "opt_bus.h"
 #include <sys/malloc.h>
 #include <sys/kernel.h>
 #include <sys/module.h>
-#ifdef DEVICE_SYSCTLS
-#include <sys/sysctl.h>
-#endif
 #include <sys/kobj.h>
 #include <sys/bus_private.h>
+#include <sys/sysctl.h>
 #include <sys/systm.h>
 #include <sys/bus.h>
 #include <sys/rman.h>
@@ -47,6 +45,8 @@
 
 #include <sys/thread2.h>
 
+SYSCTL_NODE(_hw, OID_AUTO, bus, CTLFLAG_RW, NULL, NULL);
+
 MALLOC_DEFINE(M_BUS, "bus", "Bus data structures");
 
 #ifdef BUS_DEBUG
@@ -92,10 +92,6 @@ void         print_devclass_list(void);
 #define print_devclass_list()          /* nop */
 #endif
 
-#ifdef DEVICE_SYSCTLS
-static void    device_register_oids(device_t dev);
-static void    device_unregister_oids(device_t dev);
-#endif
 static void    device_attach_async(device_t dev);
 static void    device_attach_thread(void *arg);
 static int     device_doattach(device_t dev);
@@ -104,6 +100,9 @@ static int do_async_attach = 0;
 static int numasyncthreads;
 TUNABLE_INT("kern.do_async_attach", &do_async_attach);
 
+TAILQ_HEAD(,device)    bus_data_devices;
+static int bus_data_generation = 1;
+
 kobj_method_t null_methods[] = {
        { 0, 0 }
 };
@@ -143,6 +142,9 @@ devclass_find_internal(const char *classname, const char *parentname,
                dc->maxunit = 0;
                TAILQ_INIT(&dc->drivers);
                TAILQ_INSERT_TAIL(&devclasses, dc, link);
+
+               bus_data_generation_update();
+
        }
        if (parentname && dc && !dc->parent)
                dc->parent = devclass_find_internal(parentname, NULL, FALSE);
@@ -217,6 +219,7 @@ devclass_add_driver(devclass_t dc, driver_t *driver)
                }
        }
 
+       bus_data_generation_update();
        return(0);
 }
 
@@ -272,6 +275,7 @@ devclass_delete_driver(devclass_t busclass, driver_t *driver)
 
        kobj_class_uninstantiate(driver);
 
+       bus_data_generation_update();
        return(0);
 }
 
@@ -505,10 +509,6 @@ devclass_add_device(devclass_t dc, device_t dev)
        dev->devclass = dc;
        ksnprintf(dev->nameunit, buflen, "%s%d", dc->name, dev->unit);
 
-#ifdef DEVICE_SYSCTLS
-       device_register_oids(dev);
-#endif
-
        return(0);
 }
 
@@ -529,10 +529,6 @@ devclass_delete_device(devclass_t dc, device_t dev)
        kfree(dev->nameunit, M_BUS);
        dev->nameunit = NULL;
 
-#ifdef DEVICE_SYSCTLS
-       device_unregister_oids(dev);
-#endif
-
        return(0);
 }
 
@@ -583,6 +579,9 @@ make_device(device_t parent, const char *name, int unit)
 
        dev->state = DS_NOTPRESENT;
 
+       TAILQ_INSERT_TAIL(&bus_data_devices, dev, devlink);
+       bus_data_generation_update();
+
        return(dev);
 }
 
@@ -637,6 +636,7 @@ device_add_child_ordered(device_t dev, int order, const char *name, int unit)
                TAILQ_INSERT_TAIL(&dev->children, child, link);
        }
 
+       bus_data_generation_update();
        return(child);
 }
 
@@ -660,9 +660,11 @@ device_delete_child(device_t dev, device_t child)
        if (child->devclass)
                devclass_delete_device(child->devclass, child);
        TAILQ_REMOVE(&dev->children, child, link);
+       TAILQ_REMOVE(&bus_data_devices, child, devlink);
        device_set_desc(child, NULL);
        kobj_delete((kobj_t)child, M_BUS);
 
+       bus_data_generation_update();
        return(0);
 }
 
@@ -805,6 +807,8 @@ device_probe_child(device_t dev, device_t child)
                         */
                        DEVICE_PROBE(child);
                }
+
+               bus_data_generation_update();
                child->state = DS_ALIVE;
                return(0);
        }
@@ -928,17 +932,12 @@ device_set_desc_internal(device_t dev, const char* desc, int copy)
                        strcpy(dev->desc, desc);
                        dev->flags |= DF_DESCMALLOCED;
                }
-       } else
+       } else {
                /* Avoid a -Wcast-qual warning */
                dev->desc = (char *)(uintptr_t) desc;
-
-#ifdef DEVICE_SYSCTLS
-       {
-               struct sysctl_oid *oid = &dev->oid[1];
-               oid->oid_arg1 = dev->desc ? dev->desc : "";
-               oid->oid_arg2 = dev->desc ? strlen(dev->desc) : 0;
        }
-#endif
+
+       bus_data_generation_update();
 }
 
 void
@@ -1089,6 +1088,7 @@ int
 device_set_devclass(device_t dev, const char *classname)
 {
        devclass_t dc;
+       int error;
 
        if (!classname) {
                if (dev->devclass)
@@ -1105,7 +1105,10 @@ device_set_devclass(device_t dev, const char *classname)
        if (!dc)
                return(ENOMEM);
 
-       return(devclass_add_device(dc, dev));
+       error = devclass_add_device(dc, dev);
+
+       bus_data_generation_update();
+       return(error);
 }
 
 int
@@ -1135,8 +1138,11 @@ device_set_driver(device_t dev, driver_t *driver)
                                return(ENOMEM);
                        }
                }
-       } else
+       } else {
                kobj_init((kobj_t) dev, &null_class);
+       }
+
+       bus_data_generation_update();
        return(0);
 }
 
@@ -1294,129 +1300,13 @@ device_set_unit(device_t dev, int unit)
                return(err);
        dev->unit = unit;
        err = devclass_add_device(dc, dev);
-       return(err);
-}
-
-#ifdef DEVICE_SYSCTLS
-
-/*
- * Sysctl nodes for devices.
- */
-
-SYSCTL_NODE(_hw, OID_AUTO, devices, CTLFLAG_RW, 0, "A list of all devices");
-
-static int
-sysctl_handle_children(SYSCTL_HANDLER_ARGS)
-{
-       device_t dev = arg1;
-       device_t child;
-       int first = 1, error = 0;
-
-       TAILQ_FOREACH(child, &dev->children, link)
-               if (child->nameunit) {
-                       if (!first) {
-                               error = SYSCTL_OUT(req, ",", 1);
-                               if (error)
-                                       return error;
-                       } else
-                               first = 0;
-                       error = SYSCTL_OUT(req, child->nameunit,
-                                          strlen(child->nameunit));
-                       if (error)
-                               return(error);
-               }
-
-       error = SYSCTL_OUT(req, "", 1);
-
-       return(error);
-}
-
-static int
-sysctl_handle_state(SYSCTL_HANDLER_ARGS)
-{
-       device_t dev = arg1;
-
-       switch (dev->state) {
-       case DS_NOTPRESENT:
-               return SYSCTL_OUT(req, "notpresent", sizeof("notpresent"));
-       case DS_ALIVE:
-               return SYSCTL_OUT(req, "alive", sizeof("alive"));
-       case DS_INPROGRESS:
-               return SYSCTL_OUT(req, "in-progress", sizeof("in-progress"));
-       case DS_ATTACHED:
-               return SYSCTL_OUT(req, "attached", sizeof("attached"));
-       case DS_BUSY:
-               return SYSCTL_OUT(req, "busy", sizeof("busy"));
-       default:
-               return (0);
-       }
-}
-
-static void
-device_register_oids(device_t dev)
-{
-       struct sysctl_oid* oid;
-
-       oid = &dev->oid[0];
-       bzero(oid, sizeof(*oid));
-       oid->oid_parent = &sysctl__hw_devices_children;
-       oid->oid_number = OID_AUTO;
-       oid->oid_kind = CTLTYPE_NODE | CTLFLAG_RW;
-       oid->oid_arg1 = &dev->oidlist[0];
-       oid->oid_arg2 = 0;
-       oid->oid_name = dev->nameunit;
-       oid->oid_handler = 0;
-       oid->oid_fmt = "N";
-       SLIST_INIT(&dev->oidlist[0]);
-       sysctl_register_oid(oid);
-
-       oid = &dev->oid[1];
-       bzero(oid, sizeof(*oid));
-       oid->oid_parent = &dev->oidlist[0];
-       oid->oid_number = OID_AUTO;
-       oid->oid_kind = CTLTYPE_STRING | CTLFLAG_RD;
-       oid->oid_arg1 = dev->desc ? dev->desc : "";
-       oid->oid_arg2 = dev->desc ? strlen(dev->desc) : 0;
-       oid->oid_name = "desc";
-       oid->oid_handler = sysctl_handle_string;
-       oid->oid_fmt = "A";
-       sysctl_register_oid(oid);
-
-       oid = &dev->oid[2];
-       bzero(oid, sizeof(*oid));
-       oid->oid_parent = &dev->oidlist[0];
-       oid->oid_number = OID_AUTO;
-       oid->oid_kind = CTLTYPE_INT | CTLFLAG_RD;
-       oid->oid_arg1 = dev;
-       oid->oid_arg2 = 0;
-       oid->oid_name = "children";
-       oid->oid_handler = sysctl_handle_children;
-       oid->oid_fmt = "A";
-       sysctl_register_oid(oid);
-
-       oid = &dev->oid[3];
-       bzero(oid, sizeof(*oid));
-       oid->oid_parent = &dev->oidlist[0];
-       oid->oid_number = OID_AUTO;
-       oid->oid_kind = CTLTYPE_INT | CTLFLAG_RD;
-       oid->oid_arg1 = dev;
-       oid->oid_arg2 = 0;
-       oid->oid_name = "state";
-       oid->oid_handler = sysctl_handle_state;
-       oid->oid_fmt = "A";
-       sysctl_register_oid(oid);
-}
+       if (err)
+               return(err);
 
-static void
-device_unregister_oids(device_t dev)
-{
-       sysctl_unregister_oid(&dev->oid[0]);
-       sysctl_unregister_oid(&dev->oid[1]);
-       sysctl_unregister_oid(&dev->oid[2]);
+       bus_data_generation_update();
+       return(0);
 }
 
-#endif
-
 /*======================================*/
 /*
  * Access functions for device resources.
@@ -1837,6 +1727,8 @@ resource_list_delete(struct resource_list *rl,
        struct resource_list_entry *rle = resource_list_find(rl, type, rid);
 
        if (rle) {
+               if (rle->res != NULL)
+                       panic("resource_list_delete: resource has not been released");
                SLIST_REMOVE(rl, rle, resource_list_entry, link);
                kfree(rle, M_BUS);
        }
@@ -1863,6 +1755,7 @@ resource_list_alloc(struct resource_list *rl,
 
        if (!rle)
                return(0);              /* no resource of that type/rid */
+
        if (rle->res)
                panic("resource_list_alloc: resource entry is busy");
 
@@ -2641,6 +2534,7 @@ root_bus_module_handler(module_t mod, int what, void* arg)
 {
        switch (what) {
        case MOD_LOAD:
+               TAILQ_INIT(&bus_data_devices);
                root_bus = make_device(NULL, "root", 0);
                root_bus->desc = "System root bus";
                kobj_init((kobj_t) root_bus, (kobj_class_t) &root_driver);
@@ -2930,3 +2824,99 @@ resource_disabled(const char *name, int unit)
               return(0);
        return(value);
 }
+
+/*
+ * User-space access to the device tree.
+ *
+ * We implement a small set of nodes:
+ *
+ * hw.bus                      Single integer read method to obtain the
+ *                             current generation count.
+ * hw.bus.devices              Reads the entire device tree in flat space.
+ * hw.bus.rman                 Resource manager interface
+ *
+ * We might like to add the ability to scan devclasses and/or drivers to
+ * determine what else is currently loaded/available.
+ */
+
+static int
+sysctl_bus(SYSCTL_HANDLER_ARGS)
+{
+       struct u_businfo        ubus;
+
+       ubus.ub_version = BUS_USER_VERSION;
+       ubus.ub_generation = bus_data_generation;
+
+       return (SYSCTL_OUT(req, &ubus, sizeof(ubus)));
+}
+SYSCTL_NODE(_hw_bus, OID_AUTO, info, CTLFLAG_RW, sysctl_bus,
+    "bus-related data");
+
+static int
+sysctl_devices(SYSCTL_HANDLER_ARGS)
+{
+       int                     *name = (int *)arg1;
+       u_int                   namelen = arg2;
+       int                     index;
+       struct device           *dev;
+       struct u_device         udev;   /* XXX this is a bit big */
+       int                     error;
+
+       if (namelen != 2)
+               return (EINVAL);
+
+       if (bus_data_generation_check(name[0]))
+               return (EINVAL);
+
+       index = name[1];
+
+       /*
+        * Scan the list of devices, looking for the requested index.
+        */
+       TAILQ_FOREACH(dev, &bus_data_devices, devlink) {
+               if (index-- == 0)
+                       break;
+       }
+       if (dev == NULL)
+               return (ENOENT);
+
+       /*
+        * Populate the return array.
+        */
+       bzero(&udev, sizeof(udev));
+       udev.dv_handle = (uintptr_t)dev;
+       udev.dv_parent = (uintptr_t)dev->parent;
+       if (dev->nameunit != NULL)
+               strlcpy(udev.dv_name, dev->nameunit, sizeof(udev.dv_name));
+       if (dev->desc != NULL)
+               strlcpy(udev.dv_desc, dev->desc, sizeof(udev.dv_desc));
+       if (dev->driver != NULL && dev->driver->name != NULL)
+               strlcpy(udev.dv_drivername, dev->driver->name,
+                   sizeof(udev.dv_drivername));
+       bus_child_pnpinfo_str(dev, udev.dv_pnpinfo, sizeof(udev.dv_pnpinfo));
+       bus_child_location_str(dev, udev.dv_location, sizeof(udev.dv_location));
+       udev.dv_devflags = dev->devflags;
+       udev.dv_flags = dev->flags;
+       udev.dv_state = dev->state;
+       error = SYSCTL_OUT(req, &udev, sizeof(udev));
+       return (error);
+}
+
+SYSCTL_NODE(_hw_bus, OID_AUTO, devices, CTLFLAG_RD, sysctl_devices,
+    "system device tree");
+
+int
+bus_data_generation_check(int generation)
+{
+       if (generation != bus_data_generation)
+               return (1);
+
+       /* XXX generate optimised lists here? */
+       return (0);
+}
+
+void
+bus_data_generation_update(void)
+{
+       bus_data_generation++;
+}
index 86d0aed..933d134 100644 (file)
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/kern/subr_rman.c,v 1.10.2.1 2001/06/05 08:06:08 imp Exp $
- * $DragonFly: src/sys/kern/subr_rman.c,v 1.14 2008/01/05 14:02:38 swildner Exp $
+ * $DragonFly: src/sys/kern/subr_rman.c,v 1.15 2008/09/30 12:20:29 hasso Exp $
  */
 
 /*
@@ -623,3 +623,89 @@ rman_make_alignment_flags(uint32_t size)
 
        return(RF_ALIGNMENT_LOG2(i));
 }
+
+/*
+ * Sysctl interface for scanning the resource lists.
+ *
+ * We take two input parameters; the index into the list of resource
+ * managers, and the resource offset into the list.
+ */
+static int
+sysctl_rman(SYSCTL_HANDLER_ARGS)
+{
+       int                     *name = (int *)arg1;
+       u_int                   namelen = arg2;
+       int                     rman_idx, res_idx;
+       struct rman             *rm;
+       struct resource         *res;
+       struct u_rman           urm;
+       struct u_resource       ures;
+       int                     error;
+
+       if (namelen != 3)
+               return (EINVAL);
+
+       if (bus_data_generation_check(name[0]))
+               return (EINVAL);
+       rman_idx = name[1];
+       res_idx = name[2];
+
+       /*
+        * Find the indexed resource manager
+        */
+       TAILQ_FOREACH(rm, &rman_head, rm_link) {
+               if (rman_idx-- == 0)
+                       break;
+       }
+       if (rm == NULL)
+               return (ENOENT);
+
+       /*
+        * If the resource index is -1, we want details on the
+        * resource manager.
+        */
+       if (res_idx == -1) {
+               urm.rm_handle = (uintptr_t)rm;
+               strlcpy(urm.rm_descr, rm->rm_descr, RM_TEXTLEN);
+               urm.rm_start = rm->rm_start;
+               urm.rm_size = rm->rm_end - rm->rm_start + 1;
+               urm.rm_type = rm->rm_type;
+
+               error = SYSCTL_OUT(req, &urm, sizeof(urm));
+               return (error);
+       }
+
+       /*
+        * Find the indexed resource and return it.
+        */
+       CIRCLEQ_FOREACH(res, &rm->rm_list, r_link) {
+               if (res_idx-- == 0) {
+                       ures.r_handle = (uintptr_t)res;
+                       ures.r_parent = (uintptr_t)res->r_rm;
+                       ures.r_device = (uintptr_t)res->r_dev;
+                       if (res->r_dev != NULL) {
+                               if (device_get_name(res->r_dev) != NULL) {
+                                       ksnprintf(ures.r_devname, RM_TEXTLEN,
+                                           "%s%d",
+                                           device_get_name(res->r_dev),
+                                           device_get_unit(res->r_dev));
+                               } else {
+                                       strlcpy(ures.r_devname, "nomatch",
+                                           RM_TEXTLEN);
+                               }
+                       } else {
+                               ures.r_devname[0] = '\0';
+                       }
+                       ures.r_start = res->r_start;
+                       ures.r_size = res->r_end - res->r_start + 1;
+                       ures.r_flags = res->r_flags;
+
+                       error = SYSCTL_OUT(req, &ures, sizeof(ures));
+                       return (error);
+               }
+       }
+       return (ENOENT);
+}
+
+SYSCTL_NODE(_hw_bus, OID_AUTO, rman, CTLFLAG_RD, sysctl_rman,
+    "kernel resource manager");
index 5e7369c..01b0eae 100644 (file)
@@ -24,7 +24,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/sys/bus.h,v 1.30.2.5 2004/03/17 17:54:25 njl Exp $
- * $DragonFly: src/sys/sys/bus.h,v 1.29 2008/09/29 06:59:45 hasso Exp $
+ * $DragonFly: src/sys/sys/bus.h,v 1.30 2008/09/30 12:20:29 hasso Exp $
  */
 
 #ifndef _SYS_BUS_H_
@@ -61,6 +61,44 @@ typedef struct devclass              *devclass_t;
 
 typedef void driver_intr_t(void*);
 
+/*
+ * Interface information structure.
+ */
+struct u_businfo {
+       int     ub_version;             /* interface version */
+#define BUS_USER_VERSION       1
+       int     ub_generation;          /* generation count */
+};
+
+/*
+ * State of the device.
+ */
+typedef enum device_state {
+       DS_NOTPRESENT,                  /* not probed or probe failed */
+       DS_ALIVE,                       /* probe succeeded */
+       DS_INPROGRESS,                  /* attach in progress */
+       DS_ATTACHED,                    /* attach method called */
+       DS_BUSY                         /* device is open */
+} device_state_t;
+
+/*
+ * Device information exported to userspace.
+ */
+struct u_device {
+       uintptr_t       dv_handle;
+       uintptr_t       dv_parent;
+
+       char            dv_name[32];            /* Name of device in tree. */
+       char            dv_desc[32];            /* Driver description */
+       char            dv_drivername[32];      /* Driver name */
+       char            dv_pnpinfo[128];        /* Plug and play info */
+       char            dv_location[128];       /* Where is the device? */
+       uint32_t        dv_devflags;            /* API Flags for device */
+       uint16_t        dv_flags;               /* flags for dev date */
+       device_state_t  dv_state;               /* State of attachment */
+       /* XXX more driver info? */
+};
+
 /*
  * Interrupt features mask.  Note that DragonFly no longer implements
  * INTR_TYPE_* flags.
@@ -85,14 +123,6 @@ enum intr_polarity {
 
 typedef int (*devop_t)(void);
 
-typedef enum device_state {
-    DS_NOTPRESENT,                     /* not probed or probe failed */
-    DS_ALIVE,                          /* probe succeeded */
-    DS_INPROGRESS,                     /* attach in progress */
-    DS_ATTACHED,                       /* attach method called */
-    DS_BUSY                            /* device is open */
-} device_state_t;
-
 /*
  * Definitions for drivers which need to keep simple lists of resources
  * for their child devices.
@@ -381,6 +411,13 @@ int        resource_set_string(const char *name, int unit, const char *resname,
                            const char *value);
 int    resource_count(void);
 
+/*
+ * Functions for maintaining and checking consistency of
+ * bus information exported to userspace.
+ */
+int    bus_data_generation_check(int generation);
+void   bus_data_generation_update(void);
+
 /**
  * Some convenience defines for probe routines to return.  These are just
  * suggested values, and there's nothing magical about them.
index c20be86..dd407ba 100644 (file)
@@ -24,7 +24,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/sys/bus_private.h,v 1.11.2.2 2000/08/03 00:25:22 peter Exp $
- * $DragonFly: src/sys/sys/bus_private.h,v 1.9 2007/05/01 00:05:18 dillon Exp $
+ * $DragonFly: src/sys/sys/bus_private.h,v 1.10 2008/09/30 12:20:29 hasso Exp $
  */
 
 #ifndef _SYS_BUS_PRIVATE_H_
@@ -103,6 +103,7 @@ struct device {
         * Device hierarchy.
         */
        TAILQ_ENTRY(device)     link;           /* list of devices in parent */
+       TAILQ_ENTRY(device)     devlink;        /* global device list membership */
        device_t                parent;
        device_list_t           children;       /* list of subordinate devices */
 
@@ -128,10 +129,6 @@ struct device {
 #define DF_ASYNCPROBE  0x0080          /* can be probed with its own thread */
        u_char          order;          /* order from device_add_child_ordered() */
        u_char          pad;
-#ifdef DEVICE_SYSCTLS
-       struct sysctl_oid       oid[4];
-       struct sysctl_oid_list oidlist[1];
-#endif
        void            *ivars;
        void            *softc;
 };
index d02d54d..67ba8b5 100644 (file)
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/sys/rman.h,v 1.5.2.1 2001/06/05 08:06:07 imp Exp $
- * $DragonFly: src/sys/sys/rman.h,v 1.10 2006/10/25 20:56:03 dillon Exp $
+ * $DragonFly: src/sys/sys/rman.h,v 1.11 2008/09/30 12:20:29 hasso Exp $
  */
 
 #ifndef _SYS_RMAN_H_
 #include <sys/bus.h>   /* bus_space_tag_t */
 #endif
 
+#define        RF_ALLOCATED    0x0001  /* resource has been reserved */
+#define        RF_ACTIVE       0x0002  /* resource allocation has been activated */
+#define        RF_SHAREABLE    0x0004  /* resource permits contemporaneous sharing */
+#define        RF_TIMESHARE    0x0008  /* resource permits time-division sharing */
+#define        RF_WANTED       0x0010  /* somebody is waiting for this resource */
+#define        RF_FIRSTSHARE   0x0020  /* first in sharing list */
+#define        RF_PREFETCHABLE 0x0040  /* resource is prefetchable */
+#define        RF_OPTIONAL     0x0080  /* for bus_alloc_resources() */
+
+#define        RF_ALIGNMENT_SHIFT      10 /* alignment size bit starts bit 10 */
+#define        RF_ALIGNMENT_MASK       (0x003F << RF_ALIGNMENT_SHIFT)
+                               /* resource address alignemnt size bit mask */
+#define        RF_ALIGNMENT_LOG2(x)    ((x) << RF_ALIGNMENT_SHIFT)
+#define        RF_ALIGNMENT(x)         (((x) & RF_ALIGNMENT_MASK) >> RF_ALIGNMENT_SHIFT)
+
+enum   rman_type { RMAN_UNINIT = 0, RMAN_GAUGE, RMAN_ARRAY };
+
+/*
+ * String length exported to userspace for resource names, etc.
+ */
+#define RM_TEXTLEN     32
+
+/*
+ * Userspace-exported structures.
+ */
+struct u_resource {
+       uintptr_t       r_handle;               /* resource uniquifier */
+       uintptr_t       r_parent;               /* parent rman */
+       uintptr_t       r_device;               /* device owning this resource */
+       char            r_devname[RM_TEXTLEN];  /* device name XXX obsolete */
+
+       u_long          r_start;                /* offset in resource space */
+       u_long          r_size;                 /* size in resource space */
+       u_int           r_flags;                /* RF_* flags */
+};
+
+struct u_rman {
+       uintptr_t       rm_handle;              /* rman uniquifier */
+       char            rm_descr[RM_TEXTLEN];   /* rman description */
+
+       u_long          rm_start;               /* base of managed region */
+       u_long          rm_size;                /* size of managed region */
+       enum rman_type  rm_type;                /* region type */
+};
+
+#ifdef _KERNEL
 /*
  * We use a linked list rather than a bitmap because we need to be able to
  * represent potentially huge objects (like all of a processor's physical
@@ -64,22 +110,6 @@ struct      resource {
        struct  rman *r_rm;     /* resource manager from whence this came */
 };
 
-#define        RF_ALLOCATED    0x0001  /* resource has been reserved */
-#define        RF_ACTIVE       0x0002  /* resource allocation has been activated */
-#define        RF_SHAREABLE    0x0004  /* resource permits contemporaneous sharing */
-#define        RF_TIMESHARE    0x0008  /* resource permits time-division sharing */
-#define        RF_WANTED       0x0010  /* somebody is waiting for this resource */
-#define        RF_FIRSTSHARE   0x0020  /* first in sharing list */
-#define        RF_PREFETCHABLE 0x0040  /* resource is prefetchable */
-
-#define        RF_ALIGNMENT_SHIFT      10 /* alignment size bit starts bit 10 */
-#define        RF_ALIGNMENT_MASK       (0x003F << RF_ALIGNMENT_SHIFT)
-                               /* resource address alignemnt size bit mask */
-#define        RF_ALIGNMENT_LOG2(x)    ((x) << RF_ALIGNMENT_SHIFT)
-#define        RF_ALIGNMENT(x)         (((x) & RF_ALIGNMENT_MASK) >> RF_ALIGNMENT_SHIFT)
-
-enum   rman_type { RMAN_UNINIT = 0, RMAN_GAUGE, RMAN_ARRAY };
-
 struct lwkt_token;
 struct lwkt_tokref;
 
@@ -94,8 +124,6 @@ struct       rman {
 };
 TAILQ_HEAD(rman_head, rman);
 
-#ifdef _KERNEL
-
 int    rman_activate_resource(struct resource *r);
 int    rman_await_resource(struct resource *r, struct lwkt_tokref *ilock, int slpflags, int timo);
 int    rman_deactivate_resource(struct resource *r);
index 7147496..72375ba 100644 (file)
@@ -35,7 +35,7 @@
  *
  *     @(#)sysctl.h    8.1 (Berkeley) 6/2/93
  * $FreeBSD: src/sys/sys/sysctl.h,v 1.81.2.10 2003/05/01 22:48:09 trhodes Exp $
- * $DragonFly: src/sys/sys/sysctl.h,v 1.28 2008/08/03 11:00:32 sephe Exp $
+ * $DragonFly: src/sys/sys/sysctl.h,v 1.29 2008/09/30 12:20:29 hasso Exp $
  */
 
 #ifndef _SYS_SYSCTL_H_
@@ -602,6 +602,7 @@ SYSCTL_DECL(_net);
 SYSCTL_DECL(_debug);
 SYSCTL_DECL(_debug_sizeof);
 SYSCTL_DECL(_hw);
+SYSCTL_DECL(_hw_bus);
 SYSCTL_DECL(_machdep);
 SYSCTL_DECL(_user);
 SYSCTL_DECL(_compat);
index e49032e..e54205c 100644 (file)
@@ -1,6 +1,6 @@
 #      From: @(#)Makefile      5.20 (Berkeley) 6/12/93
 # $FreeBSD: src/usr.sbin/Makefile,v 1.183.2.14 2003/04/16 11:01:51 ru Exp $
-# $DragonFly: src/usr.sbin/Makefile,v 1.48 2008/08/30 16:07:59 hasso Exp $
+# $DragonFly: src/usr.sbin/Makefile,v 1.49 2008/09/30 12:20:29 hasso Exp $
 
 .include "../sys/platform/${MACHINE_PLATFORM}/Makefile.inc"
 
@@ -32,6 +32,7 @@ SUBDIR= 802_11 \
        crunch \
        daemon \
        dconschat \
+       devinfo \
        dev_mkdb \
        dntpd \
        edquota \
diff --git a/usr.sbin/devinfo/Makefile b/usr.sbin/devinfo/Makefile
new file mode 100644 (file)
index 0000000..fb16c7b
--- /dev/null
@@ -0,0 +1,12 @@
+# $FreeBSD: src/usr.sbin/devinfo/Makefile,v 1.6 2002/05/12 14:23:15 rwatson Exp $
+# $DragonFly: src/usr.sbin/devinfo/Makefile,v 1.1 2008/09/30 12:20:29 hasso Exp $
+
+PROG=  devinfo
+MAN=   devinfo.8
+
+WARNS?=        2
+
+DPADD= ${LIBDEVINFO}
+LDADD= -ldevinfo
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/devinfo/devinfo.8 b/usr.sbin/devinfo/devinfo.8
new file mode 100644 (file)
index 0000000..d30a04d
--- /dev/null
@@ -0,0 +1,76 @@
+.\"
+.\" Copyright (c) 2002 Hiten Pandya
+.\" Copyright (c) 2002 Robert N. M. Watson
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD: src/usr.sbin/devinfo/devinfo.8,v 1.9 2006/09/29 16:46:01 ru Exp $
+.\" $DragonFly: src/usr.sbin/devinfo/devinfo.8,v 1.1 2008/09/30 12:20:29 hasso Exp $
+.\"
+.Dd September 30, 2008
+.Os
+.Dt DEVINFO 8
+.Sh NAME
+.Nm devinfo
+.Nd print information about system device configuration
+.Sh SYNOPSIS
+.Nm
+.Op Fl rv
+.Nm
+.Fl u
+.Sh DESCRIPTION
+The
+.Nm
+utility, without any arguments, shows the hierarchy of devices available
+in the system, starting from the
+.Dq nexus
+device.
+.Pp
+The following options are accepted.
+.Bl -tag -width indent
+.It Fl r
+Causes hardware resource information (such as IRQ, I/O ports, I/O memory
+addresses) to be also listed, under each device that has reserved those resources.
+.It Fl u
+Displays the same information as with
+.Fl r
+but sorts by resource type rather than by device, allowing to review the
+set of system resources by usage and available resources.
+I.e., it lists all
+the IRQ consumers together.
+.It Fl v
+Display all devices in the driver tree, not just those that are attached or
+busy.
+Without this flag, only those devices that have attached are reported.
+.El
+.Sh SEE ALSO
+.Xr systat 1 ,
+.Xr devinfo 3 ,
+.Xr iostat 8 ,
+.Xr pciconf 8 ,
+.Xr pnpinfo 8 ,
+.Xr vmstat 8 ,
+.Xr devclass 9 ,
+.Xr device 9
+.Sh AUTHORS
+.An Mike Smith Aq msmith@FreeBSD.org
diff --git a/usr.sbin/devinfo/devinfo.c b/usr.sbin/devinfo/devinfo.c
new file mode 100644 (file)
index 0000000..f5f9c36
--- /dev/null
@@ -0,0 +1,233 @@
+/*-
+ * Copyright (c) 2000, 2001 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/usr.sbin/devinfo/devinfo.c,v 1.7 2007/10/27 13:06:15 jhb Exp $
+ * $DragonFly: src/usr.sbin/devinfo/devinfo.c,v 1.1 2008/09/30 12:20:29 hasso Exp $
+ */
+
+/*
+ * Print information about system device configuration.
+ */
+
+#include <sys/types.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "devinfo.h"
+
+int    rflag;
+int    vflag;
+
+static void    print_resource(struct devinfo_res *);
+static int     print_device_matching_resource(struct devinfo_res *, void *);
+static int     print_device_rman_resources(struct devinfo_rman *, void *);
+static int     print_device(struct devinfo_dev *, void *);
+static int     print_rman_resource(struct devinfo_res *, void *);
+static int     print_rman(struct devinfo_rman *, void *);
+
+struct indent_arg
+{
+       int     indent;
+       void    *arg;
+};
+
+/*
+ * Print a resource.
+ */
+void
+print_resource(struct devinfo_res *res)
+{
+       struct devinfo_rman     *rman;
+       int                     hexmode;
+
+       rman = devinfo_handle_to_rman(res->dr_rman);
+       hexmode =  (rman->dm_size > 1000) || (rman->dm_size == 0);
+       printf(hexmode ? "0x%lx" : "%lu", res->dr_start);
+       if (res->dr_size > 1)
+               printf(hexmode ? "-0x%lx" : "-%lu",
+                   res->dr_start + res->dr_size - 1);
+}
+
+/*
+ * Print resource information if this resource matches the
+ * given device.
+ *
+ * If the given indent is 0, return an indicator that a matching
+ * resource exists.
+ */
+int
+print_device_matching_resource(struct devinfo_res *res, void *arg)
+{
+       struct indent_arg       *ia = (struct indent_arg *)arg;
+       struct devinfo_dev      *dev = (struct devinfo_dev *)ia->arg;
+       int                     i;
+
+       if (devinfo_handle_to_device(res->dr_device) == dev) {
+               /* in 'detect' mode, found a match */
+               if (ia->indent == 0)
+                       return(1);
+               for (i = 0; i < ia->indent; i++)
+                       printf(" ");
+               print_resource(res);
+               printf("\n");
+       }
+       return(0);
+}
+
+/*
+ * Print resource information for this device and resource manager.
+ */
+int
+print_device_rman_resources(struct devinfo_rman *rman, void *arg)
+{
+       struct indent_arg       *ia = (struct indent_arg *)arg;
+       int                     indent, i;
+
+       indent = ia->indent;
+
+       /* check whether there are any resources matching this device */
+       ia->indent = 0;
+       if (devinfo_foreach_rman_resource(rman,
+           print_device_matching_resource, ia) != 0) {
+
+               /* there are, print header */
+               for (i = 0; i < indent; i++)
+                       printf(" ");
+               printf("%s:\n", rman->dm_desc);
+
+               /* print resources */
+               ia->indent = indent + 4;
+               devinfo_foreach_rman_resource(rman,
+                   print_device_matching_resource, ia);
+       }
+       ia->indent = indent;
+       return(0);
+}
+
+/*
+ * Print information about a device.
+ */
+int
+print_device(struct devinfo_dev *dev, void *arg)
+{
+       struct indent_arg       ia;
+       int                     i, indent;
+
+       if (vflag || (dev->dd_name[0] != 0 && dev->dd_state >= DIS_ATTACHED)) {
+               indent = (int)(intptr_t)arg;
+               for (i = 0; i < indent; i++)
+                       printf(" ");
+               printf("%s", dev->dd_name[0] ? dev->dd_name : "unknown");
+               if (vflag && *dev->dd_pnpinfo)
+                       printf(" pnpinfo %s", dev->dd_pnpinfo);
+               if (vflag && *dev->dd_location)
+                       printf(" at %s", dev->dd_location);
+               printf("\n");
+               if (rflag) {
+                       ia.indent = indent + 4;
+                       ia.arg = dev;
+                       devinfo_foreach_rman(print_device_rman_resources,
+                           (void *)&ia);
+               }
+       }
+
+       return(devinfo_foreach_device_child(dev, print_device,
+           (void *)((char *)arg + 2)));
+}
+
+/*
+ * Print information about a resource under a resource manager.
+ */
+int
+print_rman_resource(struct devinfo_res *res, void *arg __unused)
+{
+       struct devinfo_dev      *dev;
+       
+       printf("    ");
+       print_resource(res);
+       dev = devinfo_handle_to_device(res->dr_device);
+       if ((dev != NULL) && (dev->dd_name[0] != 0)) {
+               printf(" (%s)", dev->dd_name);
+       } else {
+               printf(" ----");
+       }
+       printf("\n");
+       return(0);
+}
+
+/*
+ * Print information about a resource manager.
+ */
+int
+print_rman(struct devinfo_rman *rman, void *arg __unused)
+{
+       printf("%s:\n", rman->dm_desc);
+       devinfo_foreach_rman_resource(rman, print_rman_resource, 0);
+       return(0);
+}
+
+int
+main(int argc, char *argv[]) 
+{
+       struct devinfo_dev      *root;
+       int                     c, uflag;
+
+       uflag = 0;
+       while ((c = getopt(argc, argv, "ruv")) != -1) {
+               switch(c) {
+               case 'r':
+                       rflag++;
+                       break;
+               case 'u':
+                       uflag++;
+                       break;
+               case 'v':
+                       vflag++;
+                       break;
+               default:
+                       fprintf(stderr, "%s\n%s\n",
+                           "usage: devinfo [-rv]",
+                           "       devinfo -u");
+                       exit(1);
+               }
+       }
+
+       if (devinfo_init())
+               err(1, "devinfo_init");
+
+       if ((root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE)) == NULL)
+               errx(1, "can't find root device");
+
+       /* print resource usage? */
+       if (uflag) {
+               devinfo_foreach_rman(print_rman, NULL);
+       } else {
+               /* print device hierarchy */
+               devinfo_foreach_device_child(root, print_device, (void *)0);
+       }
+       return(0);
+}
index 0fc2865..ad4e1e4 100644 (file)
@@ -1,5 +1,5 @@
 .\" $FreeBSD: src/usr.sbin/pciconf/pciconf.8,v 1.8.2.9 2003/03/11 22:31:30 trhodes Exp $
-.\" $DragonFly: src/usr.sbin/pciconf/pciconf.8,v 1.3 2008/07/23 14:14:44 swildner Exp $
+.\" $DragonFly: src/usr.sbin/pciconf/pciconf.8,v 1.4 2008/09/30 12:20:29 hasso Exp $
 .\" Copyright (c) 1997
 .\"    Stefan Esser <se@FreeBSD.org>. All rights reserved.
 .\"
@@ -178,6 +178,7 @@ This path can be overridden by setting the environment variable
 .Sh SEE ALSO
 .Xr ioctl 2 ,
 .Xr pci 4 ,
+.Xr devinfo 8 ,
 .Xr kldload 8
 .Sh HISTORY
 The