1a2e6df29SMike Smith /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 35e53a4f9SPedro F. Giffuni * 4a2e6df29SMike Smith * Copyright (c) 2000 Michael Smith 5a2e6df29SMike Smith * Copyright (c) 2000 BSDi 6a2e6df29SMike Smith * All rights reserved. 7a2e6df29SMike Smith * 8a2e6df29SMike Smith * Redistribution and use in source and binary forms, with or without 9a2e6df29SMike Smith * modification, are permitted provided that the following conditions 10a2e6df29SMike Smith * are met: 11a2e6df29SMike Smith * 1. Redistributions of source code must retain the above copyright 12a2e6df29SMike Smith * notice, this list of conditions and the following disclaimer. 13a2e6df29SMike Smith * 2. Redistributions in binary form must reproduce the above copyright 14a2e6df29SMike Smith * notice, this list of conditions and the following disclaimer in the 15a2e6df29SMike Smith * documentation and/or other materials provided with the distribution. 16a2e6df29SMike Smith * 17a2e6df29SMike Smith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18a2e6df29SMike Smith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19a2e6df29SMike Smith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20a2e6df29SMike Smith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21a2e6df29SMike Smith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22a2e6df29SMike Smith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23a2e6df29SMike Smith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24a2e6df29SMike Smith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25a2e6df29SMike Smith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26a2e6df29SMike Smith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27a2e6df29SMike Smith * SUCH DAMAGE. 28a2e6df29SMike Smith */ 29a2e6df29SMike Smith 30e67f5b9fSMatthew Dillon #include <sys/cdefs.h> 31a2e6df29SMike Smith /* 322af3af27SStefan Farfeleder * An interface to the FreeBSD kernel's bus/device information interface. 33a2e6df29SMike Smith * 34a2e6df29SMike Smith * This interface is implemented with the 35a2e6df29SMike Smith * 36a2e6df29SMike Smith * hw.bus 37a2e6df29SMike Smith * hw.bus.devices 38a2e6df29SMike Smith * hw.bus.rman 39a2e6df29SMike Smith * 40a2e6df29SMike Smith * sysctls. The interface is not meant for general user application 41a2e6df29SMike Smith * consumption. 42a2e6df29SMike Smith * 43a2e6df29SMike Smith * Device information is obtained by scanning a linear list of all devices 44a2e6df29SMike Smith * maintained by the kernel. The actual device structure pointers are 45a2e6df29SMike Smith * handed out as opaque handles in order to allow reconstruction of the 46a2e6df29SMike Smith * logical toplogy in user space. 47a2e6df29SMike Smith * 48a2e6df29SMike Smith * Resource information is obtained by scanning the kernel's resource 49a2e6df29SMike Smith * managers and fetching their contents. Ownership of resources is 50a2e6df29SMike Smith * tracked using the device's structure pointer again as a handle. 51a2e6df29SMike Smith * 52a2e6df29SMike Smith * In order to ensure coherency of the library's picture of the kernel, 53a2e6df29SMike Smith * a generation count is maintained by the kernel. The initial generation 54a2e6df29SMike Smith * count is obtained (along with the interface version) from the hw.bus 55a2e6df29SMike Smith * sysctl, and must be passed in with every request. If the generation 56a2e6df29SMike Smith * number supplied by the library does not match the kernel's current 57a2e6df29SMike Smith * generation number, the request is failed and the library must discard 58a2e6df29SMike Smith * the data it has received and rescan. 59a2e6df29SMike Smith * 60a2e6df29SMike Smith * The information obtained from the kernel is exported to consumers of 61a2e6df29SMike Smith * this library through a variety of interfaces. 62a2e6df29SMike Smith */ 63a2e6df29SMike Smith 64a2e6df29SMike Smith #include <sys/param.h> 65a2e6df29SMike Smith #include <sys/types.h> 66a2e6df29SMike Smith #include <sys/sysctl.h> 67a2e6df29SMike Smith #include <err.h> 68a2e6df29SMike Smith #include <errno.h> 69a2e6df29SMike Smith #include <stdio.h> 70a2e6df29SMike Smith #include <stdlib.h> 71e495c560SJohn Baldwin #include <string.h> 72a2e6df29SMike Smith #include "devinfo.h" 73a2e6df29SMike Smith #include "devinfo_var.h" 74a2e6df29SMike Smith 75a2e6df29SMike Smith static int devinfo_init_devices(int generation); 76a2e6df29SMike Smith static int devinfo_init_resources(int generation); 7732592d86SEric van Gyzen static void devinfo_free_dev(struct devinfo_i_dev *dd); 78a2e6df29SMike Smith 79a2e6df29SMike Smith TAILQ_HEAD(,devinfo_i_dev) devinfo_dev; 80a2e6df29SMike Smith TAILQ_HEAD(,devinfo_i_rman) devinfo_rman; 81a2e6df29SMike Smith TAILQ_HEAD(,devinfo_i_res) devinfo_res; 82a2e6df29SMike Smith 83a2e6df29SMike Smith static int devinfo_initted = 0; 84a2e6df29SMike Smith static int devinfo_generation = 0; 85a2e6df29SMike Smith 86a2e6df29SMike Smith #if 0 8760cd5863SStefan Farfeleder # define debug(...) do { \ 8860cd5863SStefan Farfeleder fprintf(stderr, "%s:", __func__); \ 8960cd5863SStefan Farfeleder fprintf(stderr, __VA_ARGS__); \ 9060cd5863SStefan Farfeleder fprintf(stderr, "\n"); \ 9160cd5863SStefan Farfeleder } while (0) 92a2e6df29SMike Smith #else 9360cd5863SStefan Farfeleder # define debug(...) 94a2e6df29SMike Smith #endif 95a2e6df29SMike Smith 96a2e6df29SMike Smith /* 97a2e6df29SMike Smith * Initialise our local database with the contents of the kernel's 98a2e6df29SMike Smith * tables. 99a2e6df29SMike Smith */ 100a2e6df29SMike Smith int 101a2e6df29SMike Smith devinfo_init(void) 102a2e6df29SMike Smith { 103a2e6df29SMike Smith struct u_businfo ubus; 104a2e6df29SMike Smith size_t ub_size; 105a2e6df29SMike Smith int error, retries; 106a2e6df29SMike Smith 107a2e6df29SMike Smith if (!devinfo_initted) { 108a2e6df29SMike Smith TAILQ_INIT(&devinfo_dev); 109a2e6df29SMike Smith TAILQ_INIT(&devinfo_rman); 110a2e6df29SMike Smith TAILQ_INIT(&devinfo_res); 111a2e6df29SMike Smith } 112a2e6df29SMike Smith 113a2e6df29SMike Smith /* 114a2e6df29SMike Smith * Get the generation count and interface version, verify that we 115a2e6df29SMike Smith * are compatible with the kernel. 116a2e6df29SMike Smith */ 117a2e6df29SMike Smith for (retries = 0; retries < 10; retries++) { 118a2e6df29SMike Smith debug("get interface version"); 119a2e6df29SMike Smith ub_size = sizeof(ubus); 120a2e6df29SMike Smith if (sysctlbyname("hw.bus.info", &ubus, 121a2e6df29SMike Smith &ub_size, NULL, 0) != 0) { 122a2e6df29SMike Smith warn("sysctlbyname(\"hw.bus.info\", ...) failed"); 123a2e6df29SMike Smith return(EINVAL); 124a2e6df29SMike Smith } 125a2e6df29SMike Smith if ((ub_size != sizeof(ubus)) || 126a2e6df29SMike Smith (ubus.ub_version != BUS_USER_VERSION)) { 12751a9cfbfSWarner Losh warnx("kernel bus interface version mismatch: kernel %d expected %d", 12851a9cfbfSWarner Losh ubus.ub_version, BUS_USER_VERSION); 129a2e6df29SMike Smith return(EINVAL); 130a2e6df29SMike Smith } 131a2e6df29SMike Smith debug("generation count is %d", ubus.ub_generation); 132a2e6df29SMike Smith 133a2e6df29SMike Smith /* 134a2e6df29SMike Smith * Don't rescan if the generation count hasn't changed. 135a2e6df29SMike Smith */ 136a2e6df29SMike Smith if (ubus.ub_generation == devinfo_generation) 137a2e6df29SMike Smith return(0); 138a2e6df29SMike Smith 139a2e6df29SMike Smith /* 140a2e6df29SMike Smith * Generation count changed, rescan 141a2e6df29SMike Smith */ 142a2e6df29SMike Smith devinfo_free(); 143a2e6df29SMike Smith devinfo_initted = 0; 144a2e6df29SMike Smith devinfo_generation = 0; 145a2e6df29SMike Smith 146a2e6df29SMike Smith if ((error = devinfo_init_devices(ubus.ub_generation)) != 0) { 147a2e6df29SMike Smith devinfo_free(); 148a2e6df29SMike Smith if (error == EINVAL) 149a2e6df29SMike Smith continue; 150a2e6df29SMike Smith break; 151a2e6df29SMike Smith } 152a2e6df29SMike Smith if ((error = devinfo_init_resources(ubus.ub_generation)) != 0) { 153a2e6df29SMike Smith devinfo_free(); 154a2e6df29SMike Smith if (error == EINVAL) 155a2e6df29SMike Smith continue; 156a2e6df29SMike Smith break; 157a2e6df29SMike Smith } 158a2e6df29SMike Smith devinfo_initted = 1; 159a2e6df29SMike Smith devinfo_generation = ubus.ub_generation; 160a2e6df29SMike Smith return(0); 161a2e6df29SMike Smith } 162a2e6df29SMike Smith debug("scan failed after %d retries", retries); 163a2e6df29SMike Smith errno = error; 164a2e6df29SMike Smith return(1); 165a2e6df29SMike Smith } 166a2e6df29SMike Smith 167a2e6df29SMike Smith static int 168a2e6df29SMike Smith devinfo_init_devices(int generation) 169a2e6df29SMike Smith { 170a2e6df29SMike Smith struct u_device udev; 171a2e6df29SMike Smith struct devinfo_i_dev *dd; 172a2e6df29SMike Smith int dev_idx; 173a2e6df29SMike Smith int dev_ptr; 174a2e6df29SMike Smith int name2oid[2]; 175a2e6df29SMike Smith int oid[CTL_MAXNAME + 12]; 176a2e6df29SMike Smith size_t oidlen, rlen; 177cf72c10fSWarner Losh char *name, *walker, *ep; 178109a0e6bSPhilippe Charnier int error; 179a2e6df29SMike Smith 180a2e6df29SMike Smith /* 181a2e6df29SMike Smith * Find the OID for the rman interface node. 182a2e6df29SMike Smith * This is just the usual evil, undocumented sysctl juju. 183a2e6df29SMike Smith */ 184a2e6df29SMike Smith name2oid[0] = 0; 185a2e6df29SMike Smith name2oid[1] = 3; 186a2e6df29SMike Smith oidlen = sizeof(oid); 187a2e6df29SMike Smith name = "hw.bus.devices"; 188a2e6df29SMike Smith error = sysctl(name2oid, 2, oid, &oidlen, name, strlen(name)); 189a2e6df29SMike Smith if (error < 0) { 190a2e6df29SMike Smith warnx("can't find hw.bus.devices sysctl node"); 191a2e6df29SMike Smith return(ENOENT); 192a2e6df29SMike Smith } 193a2e6df29SMike Smith oidlen /= sizeof(int); 194a2e6df29SMike Smith if (oidlen > CTL_MAXNAME) { 195a2e6df29SMike Smith warnx("hw.bus.devices oid is too large"); 196a2e6df29SMike Smith return(EINVAL); 197a2e6df29SMike Smith } 198a2e6df29SMike Smith oid[oidlen++] = generation; 199a2e6df29SMike Smith dev_ptr = oidlen++; 200a2e6df29SMike Smith 201a2e6df29SMike Smith /* 202a2e6df29SMike Smith * Scan devices. 203a2e6df29SMike Smith * 204a2e6df29SMike Smith * Stop after a fairly insane number to avoid death in the case 205a2e6df29SMike Smith * of kernel corruption. 206a2e6df29SMike Smith */ 20741ec95c4SWarner Losh for (dev_idx = 0; dev_idx < 10000; dev_idx++) { 208a2e6df29SMike Smith 209a2e6df29SMike Smith /* 210a2e6df29SMike Smith * Get the device information. 211a2e6df29SMike Smith */ 212a2e6df29SMike Smith oid[dev_ptr] = dev_idx; 213a2e6df29SMike Smith rlen = sizeof(udev); 214a2e6df29SMike Smith error = sysctl(oid, oidlen, &udev, &rlen, NULL, 0); 215a2e6df29SMike Smith if (error < 0) { 216a2e6df29SMike Smith if (errno == ENOENT) /* end of list */ 217a2e6df29SMike Smith break; 218a2e6df29SMike Smith if (errno != EINVAL) /* gen count skip, restart */ 219a2e6df29SMike Smith warn("sysctl hw.bus.devices.%d", dev_idx); 220a2e6df29SMike Smith return(errno); 221a2e6df29SMike Smith } 22292376fa7SWarner Losh if (rlen != sizeof(udev)) { 22392376fa7SWarner Losh warnx("sysctl returned wrong data %zd bytes instead of %zd", 22492376fa7SWarner Losh rlen, sizeof(udev)); 22592376fa7SWarner Losh return (EINVAL); 22692376fa7SWarner Losh } 22732592d86SEric van Gyzen if ((dd = calloc(1, sizeof(*dd))) == NULL) 228a2e6df29SMike Smith return(ENOMEM); 229a2e6df29SMike Smith dd->dd_dev.dd_handle = udev.dv_handle; 230a2e6df29SMike Smith dd->dd_dev.dd_parent = udev.dv_parent; 23143832002SWarner Losh dd->dd_dev.dd_devflags = udev.dv_devflags; 23243832002SWarner Losh dd->dd_dev.dd_flags = udev.dv_flags; 23343832002SWarner Losh dd->dd_dev.dd_state = udev.dv_state; 234cf72c10fSWarner Losh 235cf72c10fSWarner Losh walker = udev.dv_fields; 236cf72c10fSWarner Losh ep = walker + sizeof(udev.dv_fields); 237cf72c10fSWarner Losh dd->dd_name = NULL; 238cf72c10fSWarner Losh dd->dd_desc = NULL; 239cf72c10fSWarner Losh dd->dd_drivername = NULL; 240cf72c10fSWarner Losh dd->dd_pnpinfo = NULL; 241cf72c10fSWarner Losh dd->dd_location = NULL; 242cf72c10fSWarner Losh #define UNPACK(x) \ 243cf72c10fSWarner Losh dd->dd_dev.x = dd->x = strdup(walker); \ 24432592d86SEric van Gyzen if (dd->x == NULL) { \ 24532592d86SEric van Gyzen devinfo_free_dev(dd); \ 246cf72c10fSWarner Losh return(ENOMEM); \ 24732592d86SEric van Gyzen } \ 24832592d86SEric van Gyzen if (walker + strnlen(walker, ep - walker) >= ep) { \ 24932592d86SEric van Gyzen devinfo_free_dev(dd); \ 250cf72c10fSWarner Losh return(EINVAL); \ 25132592d86SEric van Gyzen } \ 252cf72c10fSWarner Losh walker += strlen(walker) + 1; 253cf72c10fSWarner Losh 254cf72c10fSWarner Losh UNPACK(dd_name); 255cf72c10fSWarner Losh UNPACK(dd_desc); 256cf72c10fSWarner Losh UNPACK(dd_drivername); 257cf72c10fSWarner Losh UNPACK(dd_pnpinfo); 258cf72c10fSWarner Losh UNPACK(dd_location); 259cf72c10fSWarner Losh #undef UNPACK 260a2e6df29SMike Smith TAILQ_INSERT_TAIL(&devinfo_dev, dd, dd_link); 261a2e6df29SMike Smith } 262a2e6df29SMike Smith debug("fetched %d devices", dev_idx); 263a2e6df29SMike Smith return(0); 264a2e6df29SMike Smith } 265a2e6df29SMike Smith 266a2e6df29SMike Smith static int 267a2e6df29SMike Smith devinfo_init_resources(int generation) 268a2e6df29SMike Smith { 269a2e6df29SMike Smith struct u_rman urman; 270a2e6df29SMike Smith struct devinfo_i_rman *dm; 271a2e6df29SMike Smith struct u_resource ures; 272a2e6df29SMike Smith struct devinfo_i_res *dr; 273a2e6df29SMike Smith int rman_idx, res_idx; 274a2e6df29SMike Smith int rman_ptr, res_ptr; 275a2e6df29SMike Smith int name2oid[2]; 276a2e6df29SMike Smith int oid[CTL_MAXNAME + 12]; 277a2e6df29SMike Smith size_t oidlen, rlen; 278109a0e6bSPhilippe Charnier char *name; 279109a0e6bSPhilippe Charnier int error; 280a2e6df29SMike Smith 281a2e6df29SMike Smith /* 282a2e6df29SMike Smith * Find the OID for the rman interface node. 283a2e6df29SMike Smith * This is just the usual evil, undocumented sysctl juju. 284a2e6df29SMike Smith */ 285a2e6df29SMike Smith name2oid[0] = 0; 286a2e6df29SMike Smith name2oid[1] = 3; 287a2e6df29SMike Smith oidlen = sizeof(oid); 288a2e6df29SMike Smith name = "hw.bus.rman"; 289a2e6df29SMike Smith error = sysctl(name2oid, 2, oid, &oidlen, name, strlen(name)); 290a2e6df29SMike Smith if (error < 0) { 291a2e6df29SMike Smith warnx("can't find hw.bus.rman sysctl node"); 292a2e6df29SMike Smith return(ENOENT); 293a2e6df29SMike Smith } 294a2e6df29SMike Smith oidlen /= sizeof(int); 295a2e6df29SMike Smith if (oidlen > CTL_MAXNAME) { 296a2e6df29SMike Smith warnx("hw.bus.rman oid is too large"); 297a2e6df29SMike Smith return(EINVAL); 298a2e6df29SMike Smith } 299a2e6df29SMike Smith oid[oidlen++] = generation; 300a2e6df29SMike Smith rman_ptr = oidlen++; 301a2e6df29SMike Smith res_ptr = oidlen++; 302a2e6df29SMike Smith 303a2e6df29SMike Smith /* 304a2e6df29SMike Smith * Scan resource managers. 305a2e6df29SMike Smith * 306a2e6df29SMike Smith * Stop after a fairly insane number to avoid death in the case 307a2e6df29SMike Smith * of kernel corruption. 308a2e6df29SMike Smith */ 309a2e6df29SMike Smith for (rman_idx = 0; rman_idx < 255; rman_idx++) { 310a2e6df29SMike Smith 311a2e6df29SMike Smith /* 312a2e6df29SMike Smith * Get the resource manager information. 313a2e6df29SMike Smith */ 314a2e6df29SMike Smith oid[rman_ptr] = rman_idx; 315a2e6df29SMike Smith oid[res_ptr] = -1; 316a2e6df29SMike Smith rlen = sizeof(urman); 317a2e6df29SMike Smith error = sysctl(oid, oidlen, &urman, &rlen, NULL, 0); 318a2e6df29SMike Smith if (error < 0) { 319a2e6df29SMike Smith if (errno == ENOENT) /* end of list */ 320a2e6df29SMike Smith break; 321a2e6df29SMike Smith if (errno != EINVAL) /* gen count skip, restart */ 322a2e6df29SMike Smith warn("sysctl hw.bus.rman.%d", rman_idx); 323a2e6df29SMike Smith return(errno); 324a2e6df29SMike Smith } 325a2e6df29SMike Smith if ((dm = malloc(sizeof(*dm))) == NULL) 326a2e6df29SMike Smith return(ENOMEM); 327a2e6df29SMike Smith dm->dm_rman.dm_handle = urman.rm_handle; 328a2e6df29SMike Smith dm->dm_rman.dm_start = urman.rm_start; 329a2e6df29SMike Smith dm->dm_rman.dm_size = urman.rm_size; 330a2e6df29SMike Smith snprintf(dm->dm_desc, DEVINFO_STRLEN, "%s", urman.rm_descr); 331a2e6df29SMike Smith dm->dm_rman.dm_desc = &dm->dm_desc[0]; 332a2e6df29SMike Smith TAILQ_INSERT_TAIL(&devinfo_rman, dm, dm_link); 333a2e6df29SMike Smith 334a2e6df29SMike Smith /* 335a2e6df29SMike Smith * Scan resources on this resource manager. 336a2e6df29SMike Smith * 337a2e6df29SMike Smith * Stop after a fairly insane number to avoid death in the case 338a2e6df29SMike Smith * of kernel corruption. 339a2e6df29SMike Smith */ 340a2e6df29SMike Smith for (res_idx = 0; res_idx < 1000; res_idx++) { 341a2e6df29SMike Smith /* 342a2e6df29SMike Smith * Get the resource information. 343a2e6df29SMike Smith */ 344a2e6df29SMike Smith oid[res_ptr] = res_idx; 345a2e6df29SMike Smith rlen = sizeof(ures); 346a2e6df29SMike Smith error = sysctl(oid, oidlen, &ures, &rlen, NULL, 0); 347a2e6df29SMike Smith if (error < 0) { 348a2e6df29SMike Smith if (errno == ENOENT) /* end of list */ 349a2e6df29SMike Smith break; 350a2e6df29SMike Smith if (errno != EINVAL) /* gen count skip */ 351a2e6df29SMike Smith warn("sysctl hw.bus.rman.%d.%d", 352a2e6df29SMike Smith rman_idx, res_idx); 353a2e6df29SMike Smith return(errno); 354a2e6df29SMike Smith } 355a2e6df29SMike Smith if ((dr = malloc(sizeof(*dr))) == NULL) 356a2e6df29SMike Smith return(ENOMEM); 357a2e6df29SMike Smith dr->dr_res.dr_handle = ures.r_handle; 358a2e6df29SMike Smith dr->dr_res.dr_rman = ures.r_parent; 359a2e6df29SMike Smith dr->dr_res.dr_device = ures.r_device; 360a2e6df29SMike Smith dr->dr_res.dr_start = ures.r_start; 361a2e6df29SMike Smith dr->dr_res.dr_size = ures.r_size; 362a2e6df29SMike Smith TAILQ_INSERT_TAIL(&devinfo_res, dr, dr_link); 363a2e6df29SMike Smith } 364a2e6df29SMike Smith debug("fetched %d resources", res_idx); 365a2e6df29SMike Smith } 366a2e6df29SMike Smith debug("scanned %d resource managers", rman_idx); 367a2e6df29SMike Smith return(0); 368a2e6df29SMike Smith } 369a2e6df29SMike Smith 370a2e6df29SMike Smith /* 37132592d86SEric van Gyzen * Free an individual dev. 37232592d86SEric van Gyzen */ 37332592d86SEric van Gyzen static void 37432592d86SEric van Gyzen devinfo_free_dev(struct devinfo_i_dev *dd) 37532592d86SEric van Gyzen { 37632592d86SEric van Gyzen free(dd->dd_name); 37732592d86SEric van Gyzen free(dd->dd_desc); 37832592d86SEric van Gyzen free(dd->dd_drivername); 37932592d86SEric van Gyzen free(dd->dd_pnpinfo); 38032592d86SEric van Gyzen free(dd->dd_location); 38132592d86SEric van Gyzen free(dd); 38232592d86SEric van Gyzen } 38332592d86SEric van Gyzen 38432592d86SEric van Gyzen /* 385a2e6df29SMike Smith * Free the list contents. 386a2e6df29SMike Smith */ 387a2e6df29SMike Smith void 388a2e6df29SMike Smith devinfo_free(void) 389a2e6df29SMike Smith { 390a2e6df29SMike Smith struct devinfo_i_dev *dd; 391a2e6df29SMike Smith struct devinfo_i_rman *dm; 392a2e6df29SMike Smith struct devinfo_i_res *dr; 393a2e6df29SMike Smith 394a2e6df29SMike Smith while ((dd = TAILQ_FIRST(&devinfo_dev)) != NULL) { 395a2e6df29SMike Smith TAILQ_REMOVE(&devinfo_dev, dd, dd_link); 39632592d86SEric van Gyzen devinfo_free_dev(dd); 397a2e6df29SMike Smith } 398a2e6df29SMike Smith while ((dm = TAILQ_FIRST(&devinfo_rman)) != NULL) { 399a2e6df29SMike Smith TAILQ_REMOVE(&devinfo_rman, dm, dm_link); 400a2e6df29SMike Smith free(dm); 401a2e6df29SMike Smith } 402a2e6df29SMike Smith while ((dr = TAILQ_FIRST(&devinfo_res)) != NULL) { 403a2e6df29SMike Smith TAILQ_REMOVE(&devinfo_res, dr, dr_link); 404a2e6df29SMike Smith free(dr); 405a2e6df29SMike Smith } 406a2e6df29SMike Smith devinfo_initted = 0; 407afc7f38cSJohn Baldwin devinfo_generation = 0; 408a2e6df29SMike Smith } 409a2e6df29SMike Smith 410a2e6df29SMike Smith /* 411a2e6df29SMike Smith * Find a device by its handle. 412a2e6df29SMike Smith */ 413a2e6df29SMike Smith struct devinfo_dev * 414a2e6df29SMike Smith devinfo_handle_to_device(devinfo_handle_t handle) 415a2e6df29SMike Smith { 416a2e6df29SMike Smith struct devinfo_i_dev *dd; 417a2e6df29SMike Smith 418a2e6df29SMike Smith /* 419a2e6df29SMike Smith * Find the root device, whose parent is NULL 420a2e6df29SMike Smith */ 421a2e6df29SMike Smith if (handle == DEVINFO_ROOT_DEVICE) { 422a2e6df29SMike Smith TAILQ_FOREACH(dd, &devinfo_dev, dd_link) 423*7554746cSJohn Baldwin if (dd->dd_dev.dd_parent == 0) 424a2e6df29SMike Smith return(&dd->dd_dev); 425a2e6df29SMike Smith return(NULL); 426a2e6df29SMike Smith } 427a2e6df29SMike Smith 428a2e6df29SMike Smith /* 429a2e6df29SMike Smith * Scan for the device 430a2e6df29SMike Smith */ 431a2e6df29SMike Smith TAILQ_FOREACH(dd, &devinfo_dev, dd_link) 432a2e6df29SMike Smith if (dd->dd_dev.dd_handle == handle) 433a2e6df29SMike Smith return(&dd->dd_dev); 434a2e6df29SMike Smith return(NULL); 435a2e6df29SMike Smith } 436a2e6df29SMike Smith 437a2e6df29SMike Smith /* 438a2e6df29SMike Smith * Find a resource by its handle. 439a2e6df29SMike Smith */ 440a2e6df29SMike Smith struct devinfo_res * 441a2e6df29SMike Smith devinfo_handle_to_resource(devinfo_handle_t handle) 442a2e6df29SMike Smith { 443a2e6df29SMike Smith struct devinfo_i_res *dr; 444a2e6df29SMike Smith 445a2e6df29SMike Smith TAILQ_FOREACH(dr, &devinfo_res, dr_link) 446a2e6df29SMike Smith if (dr->dr_res.dr_handle == handle) 447a2e6df29SMike Smith return(&dr->dr_res); 448a2e6df29SMike Smith return(NULL); 449a2e6df29SMike Smith } 450a2e6df29SMike Smith 451a2e6df29SMike Smith /* 452a2e6df29SMike Smith * Find a resource manager by its handle. 453a2e6df29SMike Smith */ 454a2e6df29SMike Smith struct devinfo_rman * 455a2e6df29SMike Smith devinfo_handle_to_rman(devinfo_handle_t handle) 456a2e6df29SMike Smith { 457a2e6df29SMike Smith struct devinfo_i_rman *dm; 458a2e6df29SMike Smith 459a2e6df29SMike Smith TAILQ_FOREACH(dm, &devinfo_rman, dm_link) 460a2e6df29SMike Smith if (dm->dm_rman.dm_handle == handle) 461a2e6df29SMike Smith return(&dm->dm_rman); 462a2e6df29SMike Smith return(NULL); 463a2e6df29SMike Smith } 464a2e6df29SMike Smith 465a2e6df29SMike Smith /* 466a2e6df29SMike Smith * Iterate over the children of a device, calling (fn) on each. If 467a2e6df29SMike Smith * (fn) returns nonzero, abort the scan and return. 468a2e6df29SMike Smith */ 469a2e6df29SMike Smith int 470a2e6df29SMike Smith devinfo_foreach_device_child(struct devinfo_dev *parent, 471a2e6df29SMike Smith int (* fn)(struct devinfo_dev *child, void *arg), 472a2e6df29SMike Smith void *arg) 473a2e6df29SMike Smith { 474a2e6df29SMike Smith struct devinfo_i_dev *dd; 475a2e6df29SMike Smith int error; 476a2e6df29SMike Smith 477a2e6df29SMike Smith TAILQ_FOREACH(dd, &devinfo_dev, dd_link) 478a2e6df29SMike Smith if (dd->dd_dev.dd_parent == parent->dd_handle) 479a2e6df29SMike Smith if ((error = fn(&dd->dd_dev, arg)) != 0) 480a2e6df29SMike Smith return(error); 481a2e6df29SMike Smith return(0); 482a2e6df29SMike Smith } 483a2e6df29SMike Smith 484a2e6df29SMike Smith /* 485a2e6df29SMike Smith * Iterate over all the resources owned by a device, calling (fn) on each. 486a2e6df29SMike Smith * If (fn) returns nonzero, abort the scan and return. 487a2e6df29SMike Smith */ 488a2e6df29SMike Smith int 489a2e6df29SMike Smith devinfo_foreach_device_resource(struct devinfo_dev *dev, 490a2e6df29SMike Smith int (* fn)(struct devinfo_dev *dev, struct devinfo_res *res, void *arg), 491a2e6df29SMike Smith void *arg) 492a2e6df29SMike Smith { 493a2e6df29SMike Smith struct devinfo_i_res *dr; 494a2e6df29SMike Smith int error; 495a2e6df29SMike Smith 496a2e6df29SMike Smith TAILQ_FOREACH(dr, &devinfo_res, dr_link) 497a2e6df29SMike Smith if (dr->dr_res.dr_device == dev->dd_handle) 498a2e6df29SMike Smith if ((error = fn(dev, &dr->dr_res, arg)) != 0) 499a2e6df29SMike Smith return(error); 500a2e6df29SMike Smith return(0); 501a2e6df29SMike Smith } 502a2e6df29SMike Smith 503a2e6df29SMike Smith /* 504a2e6df29SMike Smith * Iterate over all the resources owned by a resource manager, calling (fn) 505a2e6df29SMike Smith * on each. If (fn) returns nonzero, abort the scan and return. 506a2e6df29SMike Smith */ 507a2e6df29SMike Smith extern int 508a2e6df29SMike Smith devinfo_foreach_rman_resource(struct devinfo_rman *rman, 509a2e6df29SMike Smith int (* fn)(struct devinfo_res *res, void *arg), 510a2e6df29SMike Smith void *arg) 511a2e6df29SMike Smith { 512a2e6df29SMike Smith struct devinfo_i_res *dr; 513a2e6df29SMike Smith int error; 514a2e6df29SMike Smith 515a2e6df29SMike Smith TAILQ_FOREACH(dr, &devinfo_res, dr_link) 516a2e6df29SMike Smith if (dr->dr_res.dr_rman == rman->dm_handle) 517a2e6df29SMike Smith if ((error = fn(&dr->dr_res, arg)) != 0) 518a2e6df29SMike Smith return(error); 519a2e6df29SMike Smith return(0); 520a2e6df29SMike Smith } 521a2e6df29SMike Smith 522a2e6df29SMike Smith /* 523a2e6df29SMike Smith * Iterate over all the resource managers, calling (fn) on each. If (fn) 524a2e6df29SMike Smith * returns nonzero, abort the scan and return. 525a2e6df29SMike Smith */ 526a2e6df29SMike Smith extern int 527a2e6df29SMike Smith devinfo_foreach_rman(int (* fn)(struct devinfo_rman *rman, void *arg), 528a2e6df29SMike Smith void *arg) 529a2e6df29SMike Smith { 530a2e6df29SMike Smith struct devinfo_i_rman *dm; 531a2e6df29SMike Smith int error; 532a2e6df29SMike Smith 533a2e6df29SMike Smith TAILQ_FOREACH(dm, &devinfo_rman, dm_link) 534a2e6df29SMike Smith if ((error = fn(&dm->dm_rman, arg)) != 0) 535a2e6df29SMike Smith return(error); 536a2e6df29SMike Smith return(0); 537a2e6df29SMike Smith } 538