libthread_xu: add support for named POSIX semaphores.
[dragonfly.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  * $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 $
29  */
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(...)     do { \
87         fprintf(stderr, "%s:", __func__); \
88         fprintf(stderr, __VA_ARGS__); \
89         fprintf(stderr, "\n"); \
90 } while (0)
91 #else
92 # define debug(...)
93 #endif
94
95 /*
96  * Initialise our local database with the contents of the kernel's
97  * tables.
98  */
99 int
100 devinfo_init(void)
101 {
102         struct u_businfo        ubus;
103         size_t          ub_size;
104         int                     error, retries;
105
106         if (!devinfo_initted) {
107                 TAILQ_INIT(&devinfo_dev);
108                 TAILQ_INIT(&devinfo_rman);
109                 TAILQ_INIT(&devinfo_res);
110         }
111
112         /*
113          * Get the generation count and interface version, verify that we 
114          * are compatible with the kernel.
115          */
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");
122                         return(EINVAL);
123                 }
124                 if ((ub_size != sizeof(ubus)) ||
125                     (ubus.ub_version != BUS_USER_VERSION)) {
126                         warn("kernel bus interface version mismatch");
127                         return(EINVAL);
128                 }
129                 debug("generation count is %d", ubus.ub_generation);
130
131                 /*
132                  * Don't rescan if the generation count hasn't changed.
133                  */
134                 if (ubus.ub_generation == devinfo_generation)
135                         return(0);
136
137                 /*
138                  * Generation count changed, rescan
139                  */
140                 devinfo_free();
141                 devinfo_initted = 0;
142                 devinfo_generation = 0;
143
144                 if ((error = devinfo_init_devices(ubus.ub_generation)) != 0) {
145                         devinfo_free();
146                         if (error == EINVAL)
147                                 continue;
148                         break;
149                 }
150                 if ((error = devinfo_init_resources(ubus.ub_generation)) != 0) {
151                         devinfo_free();
152                         if (error == EINVAL)
153                                 continue;
154                         break;
155                 }
156                 devinfo_initted = 1;
157                 devinfo_generation = ubus.ub_generation;
158                 return(0);
159         }
160         debug("scan failed after %d retries", retries);
161         errno = error;
162         return(1);
163 }
164
165 static int
166 devinfo_init_devices(int generation)
167 {
168         struct u_device         udev;
169         struct devinfo_i_dev    *dd;
170         int                     dev_idx;
171         int                     dev_ptr;
172         int                     name2oid[2];
173         int                     oid[CTL_MAXNAME + 12];
174         size_t                  oidlen, rlen;
175         char                    *name;
176         int                     error;
177
178         /* 
179          * Find the OID for the rman interface node.
180          * This is just the usual evil, undocumented sysctl juju.
181          */
182         name2oid[0] = 0;
183         name2oid[1] = 3;
184         oidlen = sizeof(oid);
185         name = "hw.bus.devices";
186         error = sysctl(name2oid, 2, oid, &oidlen, name, strlen(name));
187         if (error < 0) {
188                 warnx("can't find hw.bus.devices sysctl node");
189                 return(ENOENT);
190         }
191         oidlen /= sizeof(int);
192         if (oidlen > CTL_MAXNAME) {
193                 warnx("hw.bus.devices oid is too large");
194                 return(EINVAL);
195         }
196         oid[oidlen++] = generation;
197         dev_ptr = oidlen++;
198
199         /*
200          * Scan devices.
201          *
202          * Stop after a fairly insane number to avoid death in the case
203          * of kernel corruption.
204          */
205         for (dev_idx = 0; dev_idx < 1000; dev_idx++) {
206
207                 /*
208                  * Get the device information.
209                  */
210                 oid[dev_ptr] = dev_idx;
211                 rlen = sizeof(udev);
212                 error = sysctl(oid, oidlen, &udev, &rlen, NULL, 0);
213                 if (error < 0) {
214                         if (errno == ENOENT)    /* end of list */
215                                 break;
216                         if (errno != EINVAL)    /* gen count skip, restart */
217                                 warn("sysctl hw.bus.devices.%d", dev_idx);
218                         return(errno);
219                 }
220                 if ((dd = malloc(sizeof(*dd))) == NULL)
221                         return(ENOMEM);
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",
229                     udev.dv_drivername);
230                 dd->dd_dev.dd_drivername = &dd->dd_drivername[0];
231                 snprintf(dd->dd_pnpinfo, sizeof(dd->dd_pnpinfo), "%s",
232                     udev.dv_pnpinfo);
233                 dd->dd_dev.dd_pnpinfo = &dd->dd_pnpinfo[0];
234                 snprintf(dd->dd_location, sizeof(dd->dd_location), "%s",
235                     udev.dv_location);
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);
241         }
242         debug("fetched %d devices", dev_idx);
243         return(0);
244 }
245
246 static int
247 devinfo_init_resources(int generation)
248 {
249         struct u_rman           urman;
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;
255         int                     name2oid[2];
256         int                     oid[CTL_MAXNAME + 12];
257         size_t                  oidlen, rlen;
258         char                    *name;
259         int                     error;
260
261         /* 
262          * Find the OID for the rman interface node.
263          * This is just the usual evil, undocumented sysctl juju.
264          */
265         name2oid[0] = 0;
266         name2oid[1] = 3;
267         oidlen = sizeof(oid);
268         name = "hw.bus.rman";
269         error = sysctl(name2oid, 2, oid, &oidlen, name, strlen(name));
270         if (error < 0) {
271                 warnx("can't find hw.bus.rman sysctl node");
272                 return(ENOENT);
273         }
274         oidlen /= sizeof(int);
275         if (oidlen > CTL_MAXNAME) {
276                 warnx("hw.bus.rman oid is too large");
277                 return(EINVAL);
278         }
279         oid[oidlen++] = generation;
280         rman_ptr = oidlen++;
281         res_ptr = oidlen++;
282
283         /*
284          * Scan resource managers.
285          *
286          * Stop after a fairly insane number to avoid death in the case
287          * of kernel corruption.
288          */
289         for (rman_idx = 0; rman_idx < 255; rman_idx++) {
290
291                 /*
292                  * Get the resource manager information.
293                  */
294                 oid[rman_ptr] = rman_idx;
295                 oid[res_ptr] = -1;
296                 rlen = sizeof(urman);
297                 error = sysctl(oid, oidlen, &urman, &rlen, NULL, 0);
298                 if (error < 0) {
299                         if (errno == ENOENT)    /* end of list */
300                                 break;
301                         if (errno != EINVAL)    /* gen count skip, restart */
302                                 warn("sysctl hw.bus.rman.%d", rman_idx);
303                         return(errno);
304                 }
305                 if ((dm = malloc(sizeof(*dm))) == NULL)
306                         return(ENOMEM);
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);
313
314                 /*
315                  * Scan resources on this resource manager.
316                  *
317                  * Stop after a fairly insane number to avoid death in the case
318                  * of kernel corruption.
319                  */
320                 for (res_idx = 0; res_idx < 1000; res_idx++) {
321                         /* 
322                          * Get the resource information.
323                          */
324                         oid[res_ptr] = res_idx;
325                         rlen = sizeof(ures);
326                         error = sysctl(oid, oidlen, &ures, &rlen, NULL, 0);
327                         if (error < 0) {
328                                 if (errno == ENOENT)    /* end of list */
329                                         break;
330                                 if (errno != EINVAL)    /* gen count skip */
331                                         warn("sysctl hw.bus.rman.%d.%d",
332                                             rman_idx, res_idx);
333                                 return(errno);
334                         }
335                         if ((dr = malloc(sizeof(*dr))) == NULL)
336                                 return(ENOMEM);
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);
343                 }
344                 debug("fetched %d resources", res_idx);
345         }
346         debug("scanned %d resource managers", rman_idx);
347         return(0);
348 }
349
350 /*
351  * Free the list contents.
352  */
353 void
354 devinfo_free(void)
355 {
356         struct devinfo_i_dev    *dd;
357         struct devinfo_i_rman   *dm;
358         struct devinfo_i_res    *dr;
359
360         while ((dd = TAILQ_FIRST(&devinfo_dev)) != NULL) {
361                 TAILQ_REMOVE(&devinfo_dev, dd, dd_link);
362                 free(dd);
363         }
364         while ((dm = TAILQ_FIRST(&devinfo_rman)) != NULL) {
365                 TAILQ_REMOVE(&devinfo_rman, dm, dm_link);
366                 free(dm);
367         }
368         while ((dr = TAILQ_FIRST(&devinfo_res)) != NULL) {
369                 TAILQ_REMOVE(&devinfo_res, dr, dr_link);
370                 free(dr);
371         }
372         devinfo_initted = 0;
373         devinfo_generation = 0;
374 }
375
376 /*
377  * Find a device by its handle.
378  */
379 struct devinfo_dev *
380 devinfo_handle_to_device(devinfo_handle_t handle)
381 {
382         struct devinfo_i_dev    *dd;
383
384         /*
385          * Find the root device, whose parent is NULL
386          */
387         if (handle == DEVINFO_ROOT_DEVICE) {
388                 TAILQ_FOREACH(dd, &devinfo_dev, dd_link)
389                     if (dd->dd_dev.dd_parent == DEVINFO_ROOT_DEVICE)
390                             return(&dd->dd_dev);
391                 return(NULL);
392         }
393
394         /*
395          * Scan for the device
396          */
397         TAILQ_FOREACH(dd, &devinfo_dev, dd_link)
398             if (dd->dd_dev.dd_handle == handle)
399                     return(&dd->dd_dev);
400         return(NULL);
401 }
402
403 /*
404  * Find a resource by its handle.
405  */
406 struct devinfo_res *
407 devinfo_handle_to_resource(devinfo_handle_t handle)
408 {
409         struct devinfo_i_res    *dr;
410
411         TAILQ_FOREACH(dr, &devinfo_res, dr_link)
412             if (dr->dr_res.dr_handle == handle)
413                     return(&dr->dr_res);
414         return(NULL);
415 }
416
417 /*
418  * Find a resource manager by its handle.
419  */
420 struct devinfo_rman *
421 devinfo_handle_to_rman(devinfo_handle_t handle)
422 {
423         struct devinfo_i_rman   *dm;
424
425         TAILQ_FOREACH(dm, &devinfo_rman, dm_link)
426             if (dm->dm_rman.dm_handle == handle)
427                     return(&dm->dm_rman);
428         return(NULL);
429 }
430
431 /*
432  * Iterate over the children of a device, calling (fn) on each.  If
433  * (fn) returns nonzero, abort the scan and return.
434  */
435 int
436 devinfo_foreach_device_child(struct devinfo_dev *parent, 
437     int (* fn)(struct devinfo_dev *child, void *arg), 
438     void *arg)
439 {
440         struct devinfo_i_dev    *dd;
441         int                             error;
442
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)
446                             return(error);
447         return(0);
448 }
449
450 /*
451  * Iterate over all the resources owned by a device, calling (fn) on each.
452  * If (fn) returns nonzero, abort the scan and return.
453  */
454 int
455 devinfo_foreach_device_resource(struct devinfo_dev *dev,
456     int (* fn)(struct devinfo_dev *dev, struct devinfo_res *res, void *arg),
457     void *arg)
458 {
459         struct devinfo_i_res    *dr;
460         int                             error;
461
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)
465                             return(error);
466         return(0);
467 }
468
469 /*
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.
472  */
473 extern int
474 devinfo_foreach_rman_resource(struct devinfo_rman *rman,
475     int (* fn)(struct devinfo_res *res, void *arg),
476     void *arg)
477 {
478         struct devinfo_i_res    *dr;
479         int                             error;
480
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)
484                             return(error);
485         return(0);
486 }
487
488 /*
489  * Iterate over all the resource managers, calling (fn) on each.  If (fn)
490  * returns nonzero, abort the scan and return.
491  */
492 extern int
493 devinfo_foreach_rman(int (* fn)(struct devinfo_rman *rman, void *arg),
494     void *arg)
495 {
496     struct devinfo_i_rman       *dm;
497     int                         error;
498
499     TAILQ_FOREACH(dm, &devinfo_rman, dm_link)
500         if ((error = fn(&dm->dm_rman, arg)) != 0)
501             return(error);
502     return(0);
503 }