2 * Copyright (c) 2000 Michael Smith
3 * Copyright (c) 2000 BSDi
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
32 * An interface to the FreeBSD kernel's bus/device information interface.
34 * This interface is implemented with the
40 * sysctls. The interface is not meant for general user application
43 * Device information is obtained by scanning a linear list of all devices
44 * maintained by the kernel. The actual device structure pointers are
45 * handed out as opaque handles in order to allow reconstruction of the
46 * logical toplogy in user space.
48 * Resource information is obtained by scanning the kernel's resource
49 * managers and fetching their contents. Ownership of resources is
50 * tracked using the device's structure pointer again as a handle.
52 * In order to ensure coherency of the library's picture of the kernel,
53 * a generation count is maintained by the kernel. The initial generation
54 * count is obtained (along with the interface version) from the hw.bus
55 * sysctl, and must be passed in with every request. If the generation
56 * number supplied by the library does not match the kernel's current
57 * generation number, the request is failed and the library must discard
58 * the data it has received and rescan.
60 * The information obtained from the kernel is exported to consumers of
61 * this library through a variety of interfaces.
64 #include <sys/param.h>
65 #include <sys/types.h>
66 #include <sys/sysctl.h>
73 #include "devinfo_var.h"
75 static int devinfo_init_devices(int generation);
76 static int devinfo_init_resources(int generation);
78 TAILQ_HEAD(,devinfo_i_dev) devinfo_dev;
79 TAILQ_HEAD(,devinfo_i_rman) devinfo_rman;
80 TAILQ_HEAD(,devinfo_i_res) devinfo_res;
82 static int devinfo_initted = 0;
83 static int devinfo_generation = 0;
86 # define debug(fmt, args...) \
87 fprintf(stderr, "%s:" fmt "\n", __func__ , ##args)
89 # define debug(fmt, args...)
93 * Initialise our local database with the contents of the kernel's
99 struct u_businfo ubus;
103 if (!devinfo_initted) {
104 TAILQ_INIT(&devinfo_dev);
105 TAILQ_INIT(&devinfo_rman);
106 TAILQ_INIT(&devinfo_res);
110 * Get the generation count and interface version, verify that we
111 * are compatible with the kernel.
113 for (retries = 0; retries < 10; retries++) {
114 debug("get interface version");
115 ub_size = sizeof(ubus);
116 if (sysctlbyname("hw.bus.info", &ubus,
117 &ub_size, NULL, 0) != 0) {
118 warn("sysctlbyname(\"hw.bus.info\", ...) failed");
121 if ((ub_size != sizeof(ubus)) ||
122 (ubus.ub_version != BUS_USER_VERSION)) {
123 warn("kernel bus interface version mismatch");
126 debug("generation count is %d", ubus.ub_generation);
129 * Don't rescan if the generation count hasn't changed.
131 if (ubus.ub_generation == devinfo_generation)
135 * Generation count changed, rescan
139 devinfo_generation = 0;
141 if ((error = devinfo_init_devices(ubus.ub_generation)) != 0) {
147 if ((error = devinfo_init_resources(ubus.ub_generation)) != 0) {
154 devinfo_generation = ubus.ub_generation;
157 debug("scan failed after %d retries", retries);
163 devinfo_init_devices(int generation)
165 struct u_device udev;
166 struct devinfo_i_dev *dd;
170 int oid[CTL_MAXNAME + 12];
176 * Find the OID for the rman interface node.
177 * This is just the usual evil, undocumented sysctl juju.
181 oidlen = sizeof(oid);
182 name = "hw.bus.devices";
183 error = sysctl(name2oid, 2, oid, &oidlen, name, strlen(name));
185 warnx("can't find hw.bus.devices sysctl node");
188 oidlen /= sizeof(int);
189 if (oidlen > CTL_MAXNAME) {
190 warnx("hw.bus.devices oid is too large");
193 oid[oidlen++] = generation;
199 * Stop after a fairly insane number to avoid death in the case
200 * of kernel corruption.
202 for (dev_idx = 0; dev_idx < 1000; dev_idx++) {
205 * Get the device information.
207 oid[dev_ptr] = dev_idx;
209 error = sysctl(oid, oidlen, &udev, &rlen, NULL, 0);
211 if (errno == ENOENT) /* end of list */
213 if (errno != EINVAL) /* gen count skip, restart */
214 warn("sysctl hw.bus.devices.%d", dev_idx);
217 if ((dd = malloc(sizeof(*dd))) == NULL)
219 dd->dd_dev.dd_handle = udev.dv_handle;
220 dd->dd_dev.dd_parent = udev.dv_parent;
221 snprintf(dd->dd_name, sizeof(dd->dd_name), "%s", udev.dv_name);
222 dd->dd_dev.dd_name = &dd->dd_name[0];
223 snprintf(dd->dd_desc, sizeof(dd->dd_desc), "%s", udev.dv_desc);
224 dd->dd_dev.dd_desc = &dd->dd_desc[0];
225 snprintf(dd->dd_drivername, sizeof(dd->dd_drivername), "%s",
227 dd->dd_dev.dd_drivername = &dd->dd_drivername[0];
228 snprintf(dd->dd_pnpinfo, sizeof(dd->dd_pnpinfo), "%s",
230 dd->dd_dev.dd_pnpinfo = &dd->dd_pnpinfo[0];
231 snprintf(dd->dd_location, sizeof(dd->dd_location), "%s",
233 dd->dd_dev.dd_location = &dd->dd_location[0];
234 dd->dd_dev.dd_devflags = udev.dv_devflags;
235 dd->dd_dev.dd_flags = udev.dv_flags;
236 dd->dd_dev.dd_state = udev.dv_state;
237 TAILQ_INSERT_TAIL(&devinfo_dev, dd, dd_link);
239 debug("fetched %d devices", dev_idx);
244 devinfo_init_resources(int generation)
247 struct devinfo_i_rman *dm;
248 struct u_resource ures;
249 struct devinfo_i_res *dr;
250 int rman_idx, res_idx;
251 int rman_ptr, res_ptr;
253 int oid[CTL_MAXNAME + 12];
259 * Find the OID for the rman interface node.
260 * This is just the usual evil, undocumented sysctl juju.
264 oidlen = sizeof(oid);
265 name = "hw.bus.rman";
266 error = sysctl(name2oid, 2, oid, &oidlen, name, strlen(name));
268 warnx("can't find hw.bus.rman sysctl node");
271 oidlen /= sizeof(int);
272 if (oidlen > CTL_MAXNAME) {
273 warnx("hw.bus.rman oid is too large");
276 oid[oidlen++] = generation;
281 * Scan resource managers.
283 * Stop after a fairly insane number to avoid death in the case
284 * of kernel corruption.
286 for (rman_idx = 0; rman_idx < 255; rman_idx++) {
289 * Get the resource manager information.
291 oid[rman_ptr] = rman_idx;
293 rlen = sizeof(urman);
294 error = sysctl(oid, oidlen, &urman, &rlen, NULL, 0);
296 if (errno == ENOENT) /* end of list */
298 if (errno != EINVAL) /* gen count skip, restart */
299 warn("sysctl hw.bus.rman.%d", rman_idx);
302 if ((dm = malloc(sizeof(*dm))) == NULL)
304 dm->dm_rman.dm_handle = urman.rm_handle;
305 dm->dm_rman.dm_start = urman.rm_start;
306 dm->dm_rman.dm_size = urman.rm_size;
307 snprintf(dm->dm_desc, DEVINFO_STRLEN, "%s", urman.rm_descr);
308 dm->dm_rman.dm_desc = &dm->dm_desc[0];
309 TAILQ_INSERT_TAIL(&devinfo_rman, dm, dm_link);
312 * Scan resources on this resource manager.
314 * Stop after a fairly insane number to avoid death in the case
315 * of kernel corruption.
317 for (res_idx = 0; res_idx < 1000; res_idx++) {
319 * Get the resource information.
321 oid[res_ptr] = res_idx;
323 error = sysctl(oid, oidlen, &ures, &rlen, NULL, 0);
325 if (errno == ENOENT) /* end of list */
327 if (errno != EINVAL) /* gen count skip */
328 warn("sysctl hw.bus.rman.%d.%d",
332 if ((dr = malloc(sizeof(*dr))) == NULL)
334 dr->dr_res.dr_handle = ures.r_handle;
335 dr->dr_res.dr_rman = ures.r_parent;
336 dr->dr_res.dr_device = ures.r_device;
337 dr->dr_res.dr_start = ures.r_start;
338 dr->dr_res.dr_size = ures.r_size;
339 TAILQ_INSERT_TAIL(&devinfo_res, dr, dr_link);
341 debug("fetched %d resources", res_idx);
343 debug("scanned %d resource managers", rman_idx);
348 * Free the list contents.
353 struct devinfo_i_dev *dd;
354 struct devinfo_i_rman *dm;
355 struct devinfo_i_res *dr;
357 while ((dd = TAILQ_FIRST(&devinfo_dev)) != NULL) {
358 TAILQ_REMOVE(&devinfo_dev, dd, dd_link);
361 while ((dm = TAILQ_FIRST(&devinfo_rman)) != NULL) {
362 TAILQ_REMOVE(&devinfo_rman, dm, dm_link);
365 while ((dr = TAILQ_FIRST(&devinfo_res)) != NULL) {
366 TAILQ_REMOVE(&devinfo_res, dr, dr_link);
370 devinfo_generation = 0;
374 * Find a device by its handle.
377 devinfo_handle_to_device(devinfo_handle_t handle)
379 struct devinfo_i_dev *dd;
382 * Find the root device, whose parent is NULL
384 if (handle == DEVINFO_ROOT_DEVICE) {
385 TAILQ_FOREACH(dd, &devinfo_dev, dd_link)
386 if (dd->dd_dev.dd_parent == DEVINFO_ROOT_DEVICE)
392 * Scan for the device
394 TAILQ_FOREACH(dd, &devinfo_dev, dd_link)
395 if (dd->dd_dev.dd_handle == handle)
401 * Find a resource by its handle.
404 devinfo_handle_to_resource(devinfo_handle_t handle)
406 struct devinfo_i_res *dr;
408 TAILQ_FOREACH(dr, &devinfo_res, dr_link)
409 if (dr->dr_res.dr_handle == handle)
415 * Find a resource manager by its handle.
417 struct devinfo_rman *
418 devinfo_handle_to_rman(devinfo_handle_t handle)
420 struct devinfo_i_rman *dm;
422 TAILQ_FOREACH(dm, &devinfo_rman, dm_link)
423 if (dm->dm_rman.dm_handle == handle)
424 return(&dm->dm_rman);
429 * Iterate over the children of a device, calling (fn) on each. If
430 * (fn) returns nonzero, abort the scan and return.
433 devinfo_foreach_device_child(struct devinfo_dev *parent,
434 int (* fn)(struct devinfo_dev *child, void *arg),
437 struct devinfo_i_dev *dd;
440 TAILQ_FOREACH(dd, &devinfo_dev, dd_link)
441 if (dd->dd_dev.dd_parent == parent->dd_handle)
442 if ((error = fn(&dd->dd_dev, arg)) != 0)
448 * Iterate over all the resources owned by a device, calling (fn) on each.
449 * If (fn) returns nonzero, abort the scan and return.
452 devinfo_foreach_device_resource(struct devinfo_dev *dev,
453 int (* fn)(struct devinfo_dev *dev, struct devinfo_res *res, void *arg),
456 struct devinfo_i_res *dr;
459 TAILQ_FOREACH(dr, &devinfo_res, dr_link)
460 if (dr->dr_res.dr_device == dev->dd_handle)
461 if ((error = fn(dev, &dr->dr_res, arg)) != 0)
467 * Iterate over all the resources owned by a resource manager, calling (fn)
468 * on each. If (fn) returns nonzero, abort the scan and return.
471 devinfo_foreach_rman_resource(struct devinfo_rman *rman,
472 int (* fn)(struct devinfo_res *res, void *arg),
475 struct devinfo_i_res *dr;
478 TAILQ_FOREACH(dr, &devinfo_res, dr_link)
479 if (dr->dr_res.dr_rman == rman->dm_handle)
480 if ((error = fn(&dr->dr_res, arg)) != 0)
486 * Iterate over all the resource managers, calling (fn) on each. If (fn)
487 * returns nonzero, abort the scan and return.
490 devinfo_foreach_rman(int (* fn)(struct devinfo_rman *rman, void *arg),
493 struct devinfo_i_rman *dm;
496 TAILQ_FOREACH(dm, &devinfo_rman, dm_link)
497 if ((error = fn(&dm->dm_rman, arg)) != 0)