chiark / gitweb /
[PATCH] libsysfs: version 2.0
[elogind.git] / libsysfs / sysfs_bus.c
1 /*
2  * sysfs_bus.c
3  *
4  * Generic bus utility functions for libsysfs
5  *
6  * Copyright (C) IBM Corp. 2003-2005
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2.1 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  *
22  */
23 #include "libsysfs.h"
24 #include "sysfs.h"
25
26 static void sysfs_close_dev(void *dev)
27 {
28         sysfs_close_device((struct sysfs_device *)dev);
29 }
30
31 static void sysfs_close_drv(void *drv)
32 {
33         sysfs_close_driver((struct sysfs_driver *)drv);
34 }
35
36 /*
37  * compares names.
38  * @a: name looked for
39  * @b: sysfs_device comparing being compared
40  * returns 1 if a==b->name or 0 not equal
41  */
42 static int name_equal(void *a, void *b)
43 {
44         if (!a || !b)
45                 return 0;
46
47         if (strcmp(((char *)a), ((struct sysfs_device *)b)->name) == 0)
48                 return 1;
49
50         return 0;
51 }
52
53 /**
54  * sysfs_close_bus: close single bus
55  * @bus: bus structure
56  */
57 void sysfs_close_bus(struct sysfs_bus *bus)
58 {
59         if (bus) {
60                 if (bus->attrlist)
61                         dlist_destroy(bus->attrlist);
62                 if (bus->devices)
63                         dlist_destroy(bus->devices);
64                 if (bus->drivers)
65                         dlist_destroy(bus->drivers);
66                 free(bus);
67         }
68 }
69
70 /**
71  * alloc_bus: mallocs new bus structure
72  * returns sysfs_bus_bus struct or NULL
73  */
74 static struct sysfs_bus *alloc_bus(void)
75 {
76         return (struct sysfs_bus *)calloc(1, sizeof(struct sysfs_bus));
77 }
78
79 /**
80  * sysfs_get_bus_devices: gets all devices for bus
81  * @bus: bus to get devices for
82  * returns dlist of devices with success and NULL with failure
83  */
84 struct dlist *sysfs_get_bus_devices(struct sysfs_bus *bus)
85 {
86         struct sysfs_device *dev;
87         struct dlist *linklist;
88         char path[SYSFS_PATH_MAX], devpath[SYSFS_PATH_MAX];
89         char target[SYSFS_PATH_MAX];
90         char *curlink;
91
92         if (!bus) {
93                 errno = EINVAL;
94                 return NULL;
95         }
96         memset(path, 0, SYSFS_PATH_MAX);
97         safestrcpy(path, bus->path);
98         safestrcat(path, "/");
99         safestrcat(path, SYSFS_DEVICES_NAME);
100
101         linklist = read_dir_links(path);
102         if (linklist) {
103                 dlist_for_each_data(linklist, curlink, char) {
104                         if (bus->devices) {
105                                 dev = (struct sysfs_device *)
106                                         dlist_find_custom(bus->devices,
107                                         (void *)curlink, name_equal);
108                                 if (dev)
109                                         continue;
110                         }
111                         safestrcpy(devpath, path);
112                         safestrcat(devpath, "/");
113                         safestrcat(devpath, curlink);
114                         if (sysfs_get_link(devpath, target, SYSFS_PATH_MAX)) {
115                                 dprintf("Error getting link - %s\n", devpath);
116                                 continue;
117                         }
118                         dev = sysfs_open_device_path(target);
119                         if (!dev) {
120                                 dprintf("Error opening device at %s\n", 
121                                                                 target);
122                                 continue;
123                         }
124                         if (!bus->devices)
125                                 bus->devices = dlist_new_with_delete
126                                         (sizeof(struct sysfs_device), 
127                                                         sysfs_close_dev);
128                         dlist_unshift_sorted(bus->devices, dev, sort_list);
129                 }
130                 sysfs_close_list(linklist);
131         }
132         return (bus->devices);
133 }
134
135 /**
136  * sysfs_get_bus_drivers: gets all drivers for bus
137  * @bus: bus to get devices for
138  * returns dlist of devices with success and NULL with failure
139  */
140 struct dlist *sysfs_get_bus_drivers(struct sysfs_bus *bus)
141 {
142         struct sysfs_driver *drv;
143         struct dlist *dirlist;
144         char path[SYSFS_PATH_MAX], drvpath[SYSFS_PATH_MAX];
145         char *curdir;
146
147         if (!bus) {
148                 errno = EINVAL;
149                 return NULL;
150         }
151         memset(path, 0, SYSFS_PATH_MAX);
152         safestrcpy(path, bus->path);
153         safestrcat(path, "/");
154         safestrcat(path, SYSFS_DRIVERS_NAME);
155
156         dirlist = read_dir_subdirs(path);
157         if (dirlist) {
158                 dlist_for_each_data(dirlist, curdir, char) {
159                         if (bus->drivers) {
160                                 drv = (struct sysfs_driver *)
161                                         dlist_find_custom(bus->drivers,
162                                         (void *)curdir, name_equal);
163                                 if (drv)
164                                         continue;
165                         }
166                         safestrcpy(drvpath, path);
167                         safestrcat(drvpath, "/");
168                         safestrcat(drvpath, curdir);
169                         drv = sysfs_open_driver_path(drvpath);
170                         if (!drv) {
171                                 dprintf("Error opening driver at %s\n", 
172                                                                 drvpath);
173                                 continue;
174                         }
175                         if (!bus->drivers)
176                                 bus->drivers = dlist_new_with_delete
177                                         (sizeof(struct sysfs_driver), 
178                                                         sysfs_close_drv);
179                         dlist_unshift_sorted(bus->drivers, drv, sort_list);
180                 }
181                 sysfs_close_list(dirlist);
182         }
183         return (bus->drivers);
184 }
185
186 /**
187  * sysfs_open_bus: opens specific bus and all its devices on system
188  * returns sysfs_bus structure with success or NULL with error.
189  */
190 struct sysfs_bus *sysfs_open_bus(const char *name)
191 {
192         struct sysfs_bus *bus;
193         char buspath[SYSFS_PATH_MAX];
194
195         if (!name) {
196                 errno = EINVAL;
197                 return NULL;
198         }
199
200         memset(buspath, 0, SYSFS_PATH_MAX);
201         if (sysfs_get_mnt_path(buspath, SYSFS_PATH_MAX)) {
202                 dprintf("Sysfs not supported on this system\n");
203                 return NULL;
204         }
205
206         safestrcat(buspath, "/");
207         safestrcat(buspath, SYSFS_BUS_NAME);
208         safestrcat(buspath, "/");
209         safestrcat(buspath, name);
210         if (sysfs_path_is_dir(buspath)) {
211                 dprintf("Invalid path to bus: %s\n", buspath);
212                 return NULL;
213         }
214         bus = alloc_bus();
215         if (!bus) {
216                 dprintf("calloc failed\n");
217                 return NULL;
218         }
219         safestrcpy(bus->name, name);    
220         safestrcpy(bus->path, buspath);
221         if (sysfs_remove_trailing_slash(bus->path)) {
222                 dprintf("Incorrect path to bus %s\n", bus->path);
223                 sysfs_close_bus(bus);
224                 return NULL;
225         }
226
227         return bus;
228 }
229
230 /**
231  * sysfs_get_bus_device: Get specific device on bus using device's id
232  * @bus: bus to find device on
233  * @id: bus_id for device
234  * returns struct sysfs_device reference or NULL if not found.
235  */
236 struct sysfs_device *sysfs_get_bus_device(struct sysfs_bus *bus, 
237                 const char *id)
238 {
239         struct sysfs_device *dev = NULL;
240         char devpath[SYSFS_PATH_MAX], target[SYSFS_PATH_MAX];
241         
242         if (!bus || !id) {
243                 errno = EINVAL;
244                 return NULL;
245         }
246
247         if (bus->devices) {
248                 dev = (struct sysfs_device *)dlist_find_custom
249                         (bus->devices, (void *)id, name_equal);
250                 if (dev)
251                         return dev;
252         }
253         safestrcpy(devpath, bus->path);
254         safestrcat(devpath, "/");
255         safestrcat(devpath, SYSFS_DEVICES_NAME);
256         safestrcat(devpath, "/");
257         safestrcat(devpath, id);
258         if (sysfs_path_is_link(devpath)) {
259                 dprintf("No such device %s on bus %s?\n", id, bus->name);
260                 return NULL;
261         }
262         if (!sysfs_get_link(devpath, target, SYSFS_PATH_MAX)) {
263                 dev = sysfs_open_device_path(target);
264                 if (!dev) {
265                         dprintf("Error opening device at %s\n", target);
266                         return NULL;
267                 }
268                 if (!bus->devices)
269                         bus->devices = dlist_new_with_delete
270                                         (sizeof(struct sysfs_device), 
271                                                         sysfs_close_dev);
272                 dlist_unshift_sorted(bus->devices, dev, sort_list);
273         }
274         return dev;
275 }
276
277 /**
278  * sysfs_get_bus_driver: Get specific driver on bus using driver name
279  * @bus: bus to find driver on
280  * @drvname: name of driver
281  * returns struct sysfs_driver reference or NULL if not found.
282  */
283 struct sysfs_driver *sysfs_get_bus_driver(struct sysfs_bus *bus,
284                 const char *drvname)
285 {
286         struct sysfs_driver *drv;
287         char drvpath[SYSFS_PATH_MAX];
288         
289         if (!bus || !drvname) {
290                 errno = EINVAL;
291                 return NULL;
292         }
293
294         if (bus->drivers) {
295                 drv = (struct sysfs_driver *)dlist_find_custom
296                         (bus->drivers, (void *)drvname, name_equal);
297                 if (drv)
298                         return drv;
299         }
300         safestrcpy(drvpath, bus->path);
301         safestrcat(drvpath, "/");
302         safestrcat(drvpath, SYSFS_DRIVERS_NAME);
303         safestrcat(drvpath, "/");
304         safestrcat(drvpath, drvname);
305         drv = sysfs_open_driver_path(drvpath);
306         if (!drv) {
307                 dprintf("Error opening driver at %s\n", drvpath);
308                 return NULL;
309         }
310         if (!bus->drivers)
311                 bus->drivers = dlist_new_with_delete
312                                 (sizeof(struct sysfs_driver), 
313                                                 sysfs_close_drv);
314         dlist_unshift_sorted(bus->drivers, drv, sort_list);
315         return drv;
316 }
317