]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libdevinfo/devinfo.c
This commit was generated by cvs2svn to compensate for changes in r147899,
[FreeBSD/FreeBSD.git] / lib / libdevinfo / devinfo.c
1 /*-
2  * Copyright (c) 2000 Michael Smith
3  * Copyright (c) 2000 BSDi
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
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.
14  *
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
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 /*
32  * An interface to the FreeBSD kernel's bus/device information interface.
33  *
34  * This interface is implemented with the
35  *
36  * hw.bus
37  * hw.bus.devices
38  * hw.bus.rman
39  * 
40  * sysctls.  The interface is not meant for general user application
41  * consumption.
42  *
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.
47  *
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.
51  *
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.
59  *
60  * The information obtained from the kernel is exported to consumers of
61  * this library through a variety of interfaces.
62  */
63
64 #include <sys/param.h>
65 #include <sys/types.h>
66 #include <sys/sysctl.h>
67 #include <err.h>
68 #include <errno.h>
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <string.h>
72 #include "devinfo.h"
73 #include "devinfo_var.h"
74
75 static int      devinfo_init_devices(int generation);
76 static int      devinfo_init_resources(int generation);
77
78 TAILQ_HEAD(,devinfo_i_dev)      devinfo_dev;
79 TAILQ_HEAD(,devinfo_i_rman)     devinfo_rman;
80 TAILQ_HEAD(,devinfo_i_res)      devinfo_res;
81
82 static int      devinfo_initted = 0;
83 static int      devinfo_generation = 0;
84
85 #if 0
86 # define debug(fmt, args...)    \
87         fprintf(stderr, "%s:" fmt "\n", __func__ , ##args)
88 #else
89 # define debug(fmt, args...)
90 #endif
91
92 /*
93  * Initialise our local database with the contents of the kernel's
94  * tables.
95  */
96 int
97 devinfo_init(void)
98 {
99         struct u_businfo        ubus;
100         size_t          ub_size;
101         int                     error, retries;
102
103         if (!devinfo_initted) {
104                 TAILQ_INIT(&devinfo_dev);
105                 TAILQ_INIT(&devinfo_rman);
106                 TAILQ_INIT(&devinfo_res);
107         }
108
109         /*
110          * Get the generation count and interface version, verify that we 
111          * are compatible with the kernel.
112          */
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");
119                         return(EINVAL);
120                 }
121                 if ((ub_size != sizeof(ubus)) ||
122                     (ubus.ub_version != BUS_USER_VERSION)) {
123                         warn("kernel bus interface version mismatch");
124                         return(EINVAL);
125                 }
126                 debug("generation count is %d", ubus.ub_generation);
127
128                 /*
129                  * Don't rescan if the generation count hasn't changed.
130                  */
131                 if (ubus.ub_generation == devinfo_generation)
132                         return(0);
133
134                 /*
135                  * Generation count changed, rescan
136                  */
137                 devinfo_free();
138                 devinfo_initted = 0;
139                 devinfo_generation = 0;
140
141                 if ((error = devinfo_init_devices(ubus.ub_generation)) != 0) {
142                         devinfo_free();
143                         if (error == EINVAL)
144                                 continue;
145                         break;
146                 }
147                 if ((error = devinfo_init_resources(ubus.ub_generation)) != 0) {
148                         devinfo_free();
149                         if (error == EINVAL)
150                                 continue;
151                         break;
152                 }
153                 devinfo_initted = 1;
154                 devinfo_generation = ubus.ub_generation;
155                 return(0);
156         }
157         debug("scan failed after %d retries", retries);
158         errno = error;
159         return(1);
160 }
161
162 static int
163 devinfo_init_devices(int generation)
164 {
165         struct u_device         udev;
166         struct devinfo_i_dev    *dd;
167         int                     dev_idx;
168         int                     dev_ptr;
169         int                     name2oid[2];
170         int                     oid[CTL_MAXNAME + 12];
171         size_t                  oidlen, rlen;
172         char                    *name;
173         int                     error;
174
175         /* 
176          * Find the OID for the rman interface node.
177          * This is just the usual evil, undocumented sysctl juju.
178          */
179         name2oid[0] = 0;
180         name2oid[1] = 3;
181         oidlen = sizeof(oid);
182         name = "hw.bus.devices";
183         error = sysctl(name2oid, 2, oid, &oidlen, name, strlen(name));
184         if (error < 0) {
185                 warnx("can't find hw.bus.devices sysctl node");
186                 return(ENOENT);
187         }
188         oidlen /= sizeof(int);
189         if (oidlen > CTL_MAXNAME) {
190                 warnx("hw.bus.devices oid is too large");
191                 return(EINVAL);
192         }
193         oid[oidlen++] = generation;
194         dev_ptr = oidlen++;
195
196         /*
197          * Scan devices.
198          *
199          * Stop after a fairly insane number to avoid death in the case
200          * of kernel corruption.
201          */
202         for (dev_idx = 0; dev_idx < 1000; dev_idx++) {
203
204                 /*
205                  * Get the device information.
206                  */
207                 oid[dev_ptr] = dev_idx;
208                 rlen = sizeof(udev);
209                 error = sysctl(oid, oidlen, &udev, &rlen, NULL, 0);
210                 if (error < 0) {
211                         if (errno == ENOENT)    /* end of list */
212                                 break;
213                         if (errno != EINVAL)    /* gen count skip, restart */
214                                 warn("sysctl hw.bus.devices.%d", dev_idx);
215                         return(errno);
216                 }
217                 if ((dd = malloc(sizeof(*dd))) == NULL)
218                         return(ENOMEM);
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",
226                     udev.dv_drivername);
227                 dd->dd_dev.dd_drivername = &dd->dd_drivername[0];
228                 snprintf(dd->dd_pnpinfo, sizeof(dd->dd_pnpinfo), "%s",
229                     udev.dv_pnpinfo);
230                 dd->dd_dev.dd_pnpinfo = &dd->dd_pnpinfo[0];
231                 snprintf(dd->dd_location, sizeof(dd->dd_location), "%s",
232                     udev.dv_location);
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);
238         }
239         debug("fetched %d devices", dev_idx);
240         return(0);
241 }
242
243 static int
244 devinfo_init_resources(int generation)
245 {
246         struct u_rman           urman;
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;
252         int                     name2oid[2];
253         int                     oid[CTL_MAXNAME + 12];
254         size_t                  oidlen, rlen;
255         char                    *name;
256         int                     error;
257
258         /* 
259          * Find the OID for the rman interface node.
260          * This is just the usual evil, undocumented sysctl juju.
261          */
262         name2oid[0] = 0;
263         name2oid[1] = 3;
264         oidlen = sizeof(oid);
265         name = "hw.bus.rman";
266         error = sysctl(name2oid, 2, oid, &oidlen, name, strlen(name));
267         if (error < 0) {
268                 warnx("can't find hw.bus.rman sysctl node");
269                 return(ENOENT);
270         }
271         oidlen /= sizeof(int);
272         if (oidlen > CTL_MAXNAME) {
273                 warnx("hw.bus.rman oid is too large");
274                 return(EINVAL);
275         }
276         oid[oidlen++] = generation;
277         rman_ptr = oidlen++;
278         res_ptr = oidlen++;
279
280         /*
281          * Scan resource managers.
282          *
283          * Stop after a fairly insane number to avoid death in the case
284          * of kernel corruption.
285          */
286         for (rman_idx = 0; rman_idx < 255; rman_idx++) {
287
288                 /*
289                  * Get the resource manager information.
290                  */
291                 oid[rman_ptr] = rman_idx;
292                 oid[res_ptr] = -1;
293                 rlen = sizeof(urman);
294                 error = sysctl(oid, oidlen, &urman, &rlen, NULL, 0);
295                 if (error < 0) {
296                         if (errno == ENOENT)    /* end of list */
297                                 break;
298                         if (errno != EINVAL)    /* gen count skip, restart */
299                                 warn("sysctl hw.bus.rman.%d", rman_idx);
300                         return(errno);
301                 }
302                 if ((dm = malloc(sizeof(*dm))) == NULL)
303                         return(ENOMEM);
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);
310
311                 /*
312                  * Scan resources on this resource manager.
313                  *
314                  * Stop after a fairly insane number to avoid death in the case
315                  * of kernel corruption.
316                  */
317                 for (res_idx = 0; res_idx < 1000; res_idx++) {
318                         /* 
319                          * Get the resource information.
320                          */
321                         oid[res_ptr] = res_idx;
322                         rlen = sizeof(ures);
323                         error = sysctl(oid, oidlen, &ures, &rlen, NULL, 0);
324                         if (error < 0) {
325                                 if (errno == ENOENT)    /* end of list */
326                                         break;
327                                 if (errno != EINVAL)    /* gen count skip */
328                                         warn("sysctl hw.bus.rman.%d.%d",
329                                             rman_idx, res_idx);
330                                 return(errno);
331                         }
332                         if ((dr = malloc(sizeof(*dr))) == NULL)
333                                 return(ENOMEM);
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);
340                 }
341                 debug("fetched %d resources", res_idx);
342         }
343         debug("scanned %d resource managers", rman_idx);
344         return(0);
345 }
346
347 /*
348  * Free the list contents.
349  */
350 void
351 devinfo_free(void)
352 {
353         struct devinfo_i_dev    *dd;
354         struct devinfo_i_rman   *dm;
355         struct devinfo_i_res    *dr;
356
357         while ((dd = TAILQ_FIRST(&devinfo_dev)) != NULL) {
358                 TAILQ_REMOVE(&devinfo_dev, dd, dd_link);
359                 free(dd);
360         }
361         while ((dm = TAILQ_FIRST(&devinfo_rman)) != NULL) {
362                 TAILQ_REMOVE(&devinfo_rman, dm, dm_link);
363                 free(dm);
364         }
365         while ((dr = TAILQ_FIRST(&devinfo_res)) != NULL) {
366                 TAILQ_REMOVE(&devinfo_res, dr, dr_link);
367                 free(dr);
368         }
369         devinfo_initted = 0;
370         devinfo_generation = 0;
371 }
372
373 /*
374  * Find a device by its handle.
375  */
376 struct devinfo_dev *
377 devinfo_handle_to_device(devinfo_handle_t handle)
378 {
379         struct devinfo_i_dev    *dd;
380
381         /*
382          * Find the root device, whose parent is NULL
383          */
384         if (handle == DEVINFO_ROOT_DEVICE) {
385                 TAILQ_FOREACH(dd, &devinfo_dev, dd_link)
386                     if (dd->dd_dev.dd_parent == DEVINFO_ROOT_DEVICE)
387                             return(&dd->dd_dev);
388                 return(NULL);
389         }
390
391         /*
392          * Scan for the device
393          */
394         TAILQ_FOREACH(dd, &devinfo_dev, dd_link)
395             if (dd->dd_dev.dd_handle == handle)
396                     return(&dd->dd_dev);
397         return(NULL);
398 }
399
400 /*
401  * Find a resource by its handle.
402  */
403 struct devinfo_res *
404 devinfo_handle_to_resource(devinfo_handle_t handle)
405 {
406         struct devinfo_i_res    *dr;
407
408         TAILQ_FOREACH(dr, &devinfo_res, dr_link)
409             if (dr->dr_res.dr_handle == handle)
410                     return(&dr->dr_res);
411         return(NULL);
412 }
413
414 /*
415  * Find a resource manager by its handle.
416  */
417 struct devinfo_rman *
418 devinfo_handle_to_rman(devinfo_handle_t handle)
419 {
420         struct devinfo_i_rman   *dm;
421
422         TAILQ_FOREACH(dm, &devinfo_rman, dm_link)
423             if (dm->dm_rman.dm_handle == handle)
424                     return(&dm->dm_rman);
425         return(NULL);
426 }
427
428 /*
429  * Iterate over the children of a device, calling (fn) on each.  If
430  * (fn) returns nonzero, abort the scan and return.
431  */
432 int
433 devinfo_foreach_device_child(struct devinfo_dev *parent, 
434     int (* fn)(struct devinfo_dev *child, void *arg), 
435     void *arg)
436 {
437         struct devinfo_i_dev    *dd;
438         int                             error;
439
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)
443                             return(error);
444         return(0);
445 }
446
447 /*
448  * Iterate over all the resources owned by a device, calling (fn) on each.
449  * If (fn) returns nonzero, abort the scan and return.
450  */
451 int
452 devinfo_foreach_device_resource(struct devinfo_dev *dev,
453     int (* fn)(struct devinfo_dev *dev, struct devinfo_res *res, void *arg),
454     void *arg)
455 {
456         struct devinfo_i_res    *dr;
457         int                             error;
458
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)
462                             return(error);
463         return(0);
464 }
465
466 /*
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.
469  */
470 extern int
471 devinfo_foreach_rman_resource(struct devinfo_rman *rman,
472     int (* fn)(struct devinfo_res *res, void *arg),
473     void *arg)
474 {
475         struct devinfo_i_res    *dr;
476         int                             error;
477
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)
481                             return(error);
482         return(0);
483 }
484
485 /*
486  * Iterate over all the resource managers, calling (fn) on each.  If (fn)
487  * returns nonzero, abort the scan and return.
488  */
489 extern int
490 devinfo_foreach_rman(int (* fn)(struct devinfo_rman *rman, void *arg),
491     void *arg)
492 {
493     struct devinfo_i_rman       *dm;
494     int                         error;
495
496     TAILQ_FOREACH(dm, &devinfo_rman, dm_link)
497         if ((error = fn(&dm->dm_rman, arg)) != 0)
498             return(error);
499     return(0);
500 }