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
27 * $FreeBSD: src/lib/libdevinfo/devinfo.c,v 1.9 2006/07/17 09:33:24 stefanf Exp $
28 * $DragonFly: src/lib/libdevinfo/devinfo.c,v 1.1 2008/09/30 12:20:29 hasso Exp $
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(...) do { \
87 fprintf(stderr, "%s:", __func__); \
88 fprintf(stderr, __VA_ARGS__); \
89 fprintf(stderr, "\n"); \
96 * Initialise our local database with the contents of the kernel's
102 struct u_businfo ubus
;
106 if (!devinfo_initted
) {
107 TAILQ_INIT(&devinfo_dev
);
108 TAILQ_INIT(&devinfo_rman
);
109 TAILQ_INIT(&devinfo_res
);
113 * Get the generation count and interface version, verify that we
114 * are compatible with the kernel.
116 for (retries
= 0; retries
< 10; retries
++) {
117 debug("get interface version");
118 ub_size
= sizeof(ubus
);
119 if (sysctlbyname("hw.bus.info", &ubus
,
120 &ub_size
, NULL
, 0) != 0) {
121 warn("sysctlbyname(\"hw.bus.info\", ...) failed");
124 if ((ub_size
!= sizeof(ubus
)) ||
125 (ubus
.ub_version
!= BUS_USER_VERSION
)) {
126 warn("kernel bus interface version mismatch");
129 debug("generation count is %d", ubus
.ub_generation
);
132 * Don't rescan if the generation count hasn't changed.
134 if (ubus
.ub_generation
== devinfo_generation
)
138 * Generation count changed, rescan
142 devinfo_generation
= 0;
144 if ((error
= devinfo_init_devices(ubus
.ub_generation
)) != 0) {
150 if ((error
= devinfo_init_resources(ubus
.ub_generation
)) != 0) {
157 devinfo_generation
= ubus
.ub_generation
;
160 debug("scan failed after %d retries", retries
);
166 devinfo_init_devices(int generation
)
168 struct u_device udev
;
169 struct devinfo_i_dev
*dd
;
173 int oid
[CTL_MAXNAME
+ 12];
179 * Find the OID for the rman interface node.
180 * This is just the usual evil, undocumented sysctl juju.
184 oidlen
= sizeof(oid
);
185 name
= "hw.bus.devices";
186 error
= sysctl(name2oid
, 2, oid
, &oidlen
, name
, strlen(name
));
188 warnx("can't find hw.bus.devices sysctl node");
191 oidlen
/= sizeof(int);
192 if (oidlen
> CTL_MAXNAME
) {
193 warnx("hw.bus.devices oid is too large");
196 oid
[oidlen
++] = generation
;
202 * Stop after a fairly insane number to avoid death in the case
203 * of kernel corruption.
205 for (dev_idx
= 0; dev_idx
< 1000; dev_idx
++) {
208 * Get the device information.
210 oid
[dev_ptr
] = dev_idx
;
212 error
= sysctl(oid
, oidlen
, &udev
, &rlen
, NULL
, 0);
214 if (errno
== ENOENT
) /* end of list */
216 if (errno
!= EINVAL
) /* gen count skip, restart */
217 warn("sysctl hw.bus.devices.%d", dev_idx
);
220 if ((dd
= malloc(sizeof(*dd
))) == NULL
)
222 dd
->dd_dev
.dd_handle
= udev
.dv_handle
;
223 dd
->dd_dev
.dd_parent
= udev
.dv_parent
;
224 snprintf(dd
->dd_name
, sizeof(dd
->dd_name
), "%s", udev
.dv_name
);
225 dd
->dd_dev
.dd_name
= &dd
->dd_name
[0];
226 snprintf(dd
->dd_desc
, sizeof(dd
->dd_desc
), "%s", udev
.dv_desc
);
227 dd
->dd_dev
.dd_desc
= &dd
->dd_desc
[0];
228 snprintf(dd
->dd_drivername
, sizeof(dd
->dd_drivername
), "%s",
230 dd
->dd_dev
.dd_drivername
= &dd
->dd_drivername
[0];
231 snprintf(dd
->dd_pnpinfo
, sizeof(dd
->dd_pnpinfo
), "%s",
233 dd
->dd_dev
.dd_pnpinfo
= &dd
->dd_pnpinfo
[0];
234 snprintf(dd
->dd_location
, sizeof(dd
->dd_location
), "%s",
236 dd
->dd_dev
.dd_location
= &dd
->dd_location
[0];
237 dd
->dd_dev
.dd_devflags
= udev
.dv_devflags
;
238 dd
->dd_dev
.dd_flags
= udev
.dv_flags
;
239 dd
->dd_dev
.dd_state
= udev
.dv_state
;
240 TAILQ_INSERT_TAIL(&devinfo_dev
, dd
, dd_link
);
242 debug("fetched %d devices", dev_idx
);
247 devinfo_init_resources(int generation
)
250 struct devinfo_i_rman
*dm
;
251 struct u_resource ures
;
252 struct devinfo_i_res
*dr
;
253 int rman_idx
, res_idx
;
254 int rman_ptr
, res_ptr
;
256 int oid
[CTL_MAXNAME
+ 12];
262 * Find the OID for the rman interface node.
263 * This is just the usual evil, undocumented sysctl juju.
267 oidlen
= sizeof(oid
);
268 name
= "hw.bus.rman";
269 error
= sysctl(name2oid
, 2, oid
, &oidlen
, name
, strlen(name
));
271 warnx("can't find hw.bus.rman sysctl node");
274 oidlen
/= sizeof(int);
275 if (oidlen
> CTL_MAXNAME
) {
276 warnx("hw.bus.rman oid is too large");
279 oid
[oidlen
++] = generation
;
284 * Scan resource managers.
286 * Stop after a fairly insane number to avoid death in the case
287 * of kernel corruption.
289 for (rman_idx
= 0; rman_idx
< 255; rman_idx
++) {
292 * Get the resource manager information.
294 oid
[rman_ptr
] = rman_idx
;
296 rlen
= sizeof(urman
);
297 error
= sysctl(oid
, oidlen
, &urman
, &rlen
, NULL
, 0);
299 if (errno
== ENOENT
) /* end of list */
301 if (errno
!= EINVAL
) /* gen count skip, restart */
302 warn("sysctl hw.bus.rman.%d", rman_idx
);
305 if ((dm
= malloc(sizeof(*dm
))) == NULL
)
307 dm
->dm_rman
.dm_handle
= urman
.rm_handle
;
308 dm
->dm_rman
.dm_start
= urman
.rm_start
;
309 dm
->dm_rman
.dm_size
= urman
.rm_size
;
310 snprintf(dm
->dm_desc
, DEVINFO_STRLEN
, "%s", urman
.rm_descr
);
311 dm
->dm_rman
.dm_desc
= &dm
->dm_desc
[0];
312 TAILQ_INSERT_TAIL(&devinfo_rman
, dm
, dm_link
);
315 * Scan resources on this resource manager.
317 * Stop after a fairly insane number to avoid death in the case
318 * of kernel corruption.
320 for (res_idx
= 0; res_idx
< 1000; res_idx
++) {
322 * Get the resource information.
324 oid
[res_ptr
] = res_idx
;
326 error
= sysctl(oid
, oidlen
, &ures
, &rlen
, NULL
, 0);
328 if (errno
== ENOENT
) /* end of list */
330 if (errno
!= EINVAL
) /* gen count skip */
331 warn("sysctl hw.bus.rman.%d.%d",
335 if ((dr
= malloc(sizeof(*dr
))) == NULL
)
337 dr
->dr_res
.dr_handle
= ures
.r_handle
;
338 dr
->dr_res
.dr_rman
= ures
.r_parent
;
339 dr
->dr_res
.dr_device
= ures
.r_device
;
340 dr
->dr_res
.dr_start
= ures
.r_start
;
341 dr
->dr_res
.dr_size
= ures
.r_size
;
342 TAILQ_INSERT_TAIL(&devinfo_res
, dr
, dr_link
);
344 debug("fetched %d resources", res_idx
);
346 debug("scanned %d resource managers", rman_idx
);
351 * Free the list contents.
356 struct devinfo_i_dev
*dd
;
357 struct devinfo_i_rman
*dm
;
358 struct devinfo_i_res
*dr
;
360 while ((dd
= TAILQ_FIRST(&devinfo_dev
)) != NULL
) {
361 TAILQ_REMOVE(&devinfo_dev
, dd
, dd_link
);
364 while ((dm
= TAILQ_FIRST(&devinfo_rman
)) != NULL
) {
365 TAILQ_REMOVE(&devinfo_rman
, dm
, dm_link
);
368 while ((dr
= TAILQ_FIRST(&devinfo_res
)) != NULL
) {
369 TAILQ_REMOVE(&devinfo_res
, dr
, dr_link
);
373 devinfo_generation
= 0;
377 * Find a device by its handle.
380 devinfo_handle_to_device(devinfo_handle_t handle
)
382 struct devinfo_i_dev
*dd
;
385 * Find the root device, whose parent is NULL
387 if (handle
== DEVINFO_ROOT_DEVICE
) {
388 TAILQ_FOREACH(dd
, &devinfo_dev
, dd_link
)
389 if (dd
->dd_dev
.dd_parent
== DEVINFO_ROOT_DEVICE
)
395 * Scan for the device
397 TAILQ_FOREACH(dd
, &devinfo_dev
, dd_link
)
398 if (dd
->dd_dev
.dd_handle
== handle
)
404 * Find a resource by its handle.
407 devinfo_handle_to_resource(devinfo_handle_t handle
)
409 struct devinfo_i_res
*dr
;
411 TAILQ_FOREACH(dr
, &devinfo_res
, dr_link
)
412 if (dr
->dr_res
.dr_handle
== handle
)
418 * Find a resource manager by its handle.
420 struct devinfo_rman
*
421 devinfo_handle_to_rman(devinfo_handle_t handle
)
423 struct devinfo_i_rman
*dm
;
425 TAILQ_FOREACH(dm
, &devinfo_rman
, dm_link
)
426 if (dm
->dm_rman
.dm_handle
== handle
)
427 return(&dm
->dm_rman
);
432 * Iterate over the children of a device, calling (fn) on each. If
433 * (fn) returns nonzero, abort the scan and return.
436 devinfo_foreach_device_child(struct devinfo_dev
*parent
,
437 int (* fn
)(struct devinfo_dev
*child
, void *arg
),
440 struct devinfo_i_dev
*dd
;
443 TAILQ_FOREACH(dd
, &devinfo_dev
, dd_link
)
444 if (dd
->dd_dev
.dd_parent
== parent
->dd_handle
)
445 if ((error
= fn(&dd
->dd_dev
, arg
)) != 0)
451 * Iterate over all the resources owned by a device, calling (fn) on each.
452 * If (fn) returns nonzero, abort the scan and return.
455 devinfo_foreach_device_resource(struct devinfo_dev
*dev
,
456 int (* fn
)(struct devinfo_dev
*dev
, struct devinfo_res
*res
, void *arg
),
459 struct devinfo_i_res
*dr
;
462 TAILQ_FOREACH(dr
, &devinfo_res
, dr_link
)
463 if (dr
->dr_res
.dr_device
== dev
->dd_handle
)
464 if ((error
= fn(dev
, &dr
->dr_res
, arg
)) != 0)
470 * Iterate over all the resources owned by a resource manager, calling (fn)
471 * on each. If (fn) returns nonzero, abort the scan and return.
474 devinfo_foreach_rman_resource(struct devinfo_rman
*rman
,
475 int (* fn
)(struct devinfo_res
*res
, void *arg
),
478 struct devinfo_i_res
*dr
;
481 TAILQ_FOREACH(dr
, &devinfo_res
, dr_link
)
482 if (dr
->dr_res
.dr_rman
== rman
->dm_handle
)
483 if ((error
= fn(&dr
->dr_res
, arg
)) != 0)
489 * Iterate over all the resource managers, calling (fn) on each. If (fn)
490 * returns nonzero, abort the scan and return.
493 devinfo_foreach_rman(int (* fn
)(struct devinfo_rman
*rman
, void *arg
),
496 struct devinfo_i_rman
*dm
;
499 TAILQ_FOREACH(dm
, &devinfo_rman
, dm_link
)
500 if ((error
= fn(&dm
->dm_rman
, arg
)) != 0)