]>
Commit | Line | Data |
---|---|---|
070f5422 GKH |
1 | /* |
2 | * sysfs_bus.c | |
3 | * | |
4 | * Generic bus utility functions for libsysfs | |
5 | * | |
fe3fe3b2 | 6 | * Copyright (C) IBM Corp. 2003 |
070f5422 GKH |
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 | ||
fe3fe3b2 DS |
26 | static void sysfs_close_dev(void *dev) |
27 | { | |
ff44a6b0 | 28 | sysfs_close_device((struct sysfs_device *)dev); |
fe3fe3b2 DS |
29 | } |
30 | ||
31 | static void sysfs_close_drv(void *drv) | |
32 | { | |
ff44a6b0 | 33 | sysfs_close_driver((struct sysfs_driver *)drv); |
fe3fe3b2 DS |
34 | } |
35 | ||
36 | /* | |
37 | * compares devices' bus ids. | |
38 | * @a: device id looking for | |
39 | * @b: sysfs_device comparing being compared | |
40 | * returns 1 if a==b->bus_id or 0 not equal | |
41 | */ | |
42 | static int bus_device_id_equal(void *a, void *b) | |
43 | { | |
44 | if (a == NULL || b == NULL) | |
45 | return 0; | |
46 | ||
47 | if (strcmp(((unsigned char *)a), ((struct sysfs_device *)b)->bus_id) | |
48 | == 0) | |
49 | return 1; | |
50 | return 0; | |
51 | } | |
52 | ||
53 | /* | |
54 | * compares drivers' names. | |
55 | * @a: driver name looking for | |
56 | * @b: sysfs_driver comparing being compared | |
57 | * returns 1 if a==b->name or 0 not equal | |
58 | */ | |
59 | static int bus_driver_name_equal(void *a, void *b) | |
60 | { | |
61 | if (a == NULL || b == NULL) | |
62 | return 0; | |
63 | ||
64 | if (strcmp(((unsigned char *)a), ((struct sysfs_driver *)b)->name) == 0) | |
65 | return 1; | |
66 | return 0; | |
67 | } | |
68 | ||
070f5422 GKH |
69 | /** |
70 | * sysfs_close_bus: close single bus | |
71 | * @bus: bus structure | |
72 | */ | |
73 | void sysfs_close_bus(struct sysfs_bus *bus) | |
74 | { | |
070f5422 GKH |
75 | if (bus != NULL) { |
76 | if (bus->directory != NULL) | |
77 | sysfs_close_directory(bus->directory); | |
fe3fe3b2 DS |
78 | if (bus->devices) |
79 | dlist_destroy(bus->devices); | |
80 | if (bus->drivers) | |
81 | dlist_destroy(bus->drivers); | |
070f5422 GKH |
82 | free(bus); |
83 | } | |
84 | } | |
85 | ||
86 | /** | |
87 | * alloc_bus: mallocs new bus structure | |
88 | * returns sysfs_bus_bus struct or NULL | |
89 | */ | |
90 | static struct sysfs_bus *alloc_bus(void) | |
91 | { | |
92 | return (struct sysfs_bus *)calloc(1, sizeof(struct sysfs_bus)); | |
93 | } | |
94 | ||
95 | /** | |
5d4754f1 DS |
96 | * sysfs_get_bus_devices: gets all devices for bus |
97 | * @bus: bus to get devices for | |
98 | * returns dlist of devices with success and NULL with failure | |
070f5422 | 99 | */ |
5d4754f1 | 100 | struct dlist *sysfs_get_bus_devices(struct sysfs_bus *bus) |
070f5422 | 101 | { |
5d4754f1 DS |
102 | struct sysfs_device *bdev = NULL; |
103 | struct sysfs_directory *devdir = NULL; | |
104 | struct sysfs_link *curl = NULL; | |
105 | unsigned char path[SYSFS_PATH_MAX]; | |
070f5422 | 106 | |
5d4754f1 | 107 | if (bus == NULL) { |
070f5422 GKH |
108 | errno = EINVAL; |
109 | return NULL; | |
110 | } | |
5d4754f1 DS |
111 | memset(path, 0, SYSFS_PATH_MAX); |
112 | strcpy(path, bus->path); | |
113 | strcat(path, "/"); | |
114 | strcat(path, SYSFS_DEVICES_NAME); | |
115 | devdir = sysfs_open_directory(path); | |
116 | if (devdir == NULL) | |
070f5422 | 117 | return NULL; |
070f5422 | 118 | |
5d4754f1 DS |
119 | if (sysfs_read_dir_links(devdir) != 0) { |
120 | sysfs_close_directory(devdir); | |
070f5422 GKH |
121 | return NULL; |
122 | } | |
fe3fe3b2 | 123 | |
bcbe2d8e DS |
124 | if (devdir->links != 0) { |
125 | dlist_for_each_data(devdir->links, curl, struct sysfs_link) { | |
126 | bdev = sysfs_open_device_path(curl->target); | |
127 | if (bdev == NULL) { | |
128 | dprintf("Error opening device at %s\n", | |
129 | curl->target); | |
130 | continue; | |
131 | } | |
132 | if (bus->devices == NULL) | |
133 | bus->devices = dlist_new_with_delete | |
134 | (sizeof(struct sysfs_device), | |
135 | sysfs_close_dev); | |
136 | dlist_unshift(bus->devices, bdev); | |
070f5422 GKH |
137 | } |
138 | } | |
5d4754f1 DS |
139 | sysfs_close_directory(devdir); |
140 | ||
141 | return (bus->devices); | |
070f5422 GKH |
142 | } |
143 | ||
144 | /** | |
5d4754f1 | 145 | * sysfs_get_bus_drivers: get all pci drivers |
070f5422 | 146 | * @bus: pci bus to add drivers to |
5d4754f1 | 147 | * returns dlist of drivers with success and NULL with error |
070f5422 | 148 | */ |
5d4754f1 | 149 | struct dlist *sysfs_get_bus_drivers(struct sysfs_bus *bus) |
070f5422 GKH |
150 | { |
151 | struct sysfs_driver *driver = NULL; | |
5d4754f1 | 152 | struct sysfs_directory *drvdir = NULL; |
fe3fe3b2 | 153 | struct sysfs_directory *cursub = NULL; |
5d4754f1 | 154 | unsigned char path[SYSFS_PATH_MAX]; |
070f5422 | 155 | |
5d4754f1 | 156 | if (bus == NULL) { |
070f5422 | 157 | errno = EINVAL; |
5d4754f1 | 158 | return NULL; |
070f5422 | 159 | } |
5d4754f1 DS |
160 | memset(path, 0, SYSFS_PATH_MAX); |
161 | strcpy(path, bus->path); | |
162 | strcat(path, "/"); | |
163 | strcat(path, SYSFS_DRIVERS_NAME); | |
164 | drvdir = sysfs_open_directory(path); | |
165 | if (drvdir == NULL) | |
166 | return NULL; | |
070f5422 | 167 | |
5d4754f1 DS |
168 | if (sysfs_read_dir_subdirs(drvdir) != 0) { |
169 | sysfs_close_directory(drvdir); | |
170 | return NULL; | |
070f5422 | 171 | } |
bcbe2d8e DS |
172 | if (drvdir->subdirs != NULL) { |
173 | dlist_for_each_data(drvdir->subdirs, cursub, | |
174 | struct sysfs_directory) { | |
175 | driver = sysfs_open_driver_path(cursub->path); | |
176 | if (driver == NULL) { | |
177 | dprintf("Error opening driver at %s\n", | |
178 | cursub->path); | |
179 | continue; | |
180 | } | |
181 | if (bus->drivers == NULL) | |
182 | bus->drivers = dlist_new_with_delete | |
183 | (sizeof(struct sysfs_driver), | |
184 | sysfs_close_drv); | |
185 | dlist_unshift(bus->drivers, driver); | |
070f5422 GKH |
186 | } |
187 | } | |
5d4754f1 DS |
188 | sysfs_close_directory(drvdir); |
189 | return (bus->drivers); | |
070f5422 GKH |
190 | } |
191 | ||
192 | /** | |
193 | * sysfs_open_bus: opens specific bus and all its devices on system | |
194 | * returns sysfs_bus structure with success or NULL with error. | |
195 | */ | |
fe3fe3b2 | 196 | struct sysfs_bus *sysfs_open_bus(const unsigned char *name) |
070f5422 GKH |
197 | { |
198 | struct sysfs_bus *bus = NULL; | |
5d4754f1 | 199 | unsigned char buspath[SYSFS_PATH_MAX]; |
070f5422 GKH |
200 | |
201 | if (name == NULL) { | |
202 | errno = EINVAL; | |
203 | return NULL; | |
204 | } | |
205 | ||
5d4754f1 DS |
206 | memset(buspath, 0, SYSFS_PATH_MAX); |
207 | if ((sysfs_get_mnt_path(buspath, SYSFS_PATH_MAX)) != 0) { | |
208 | dprintf("Sysfs not supported on this system\n"); | |
070f5422 GKH |
209 | return NULL; |
210 | } | |
5d4754f1 | 211 | |
616a7078 | 212 | strcat(buspath, "/"); |
5d4754f1 DS |
213 | strcat(buspath, SYSFS_BUS_NAME); |
214 | strcat(buspath, "/"); | |
215 | strcat(buspath, name); | |
216 | if ((sysfs_path_is_dir(buspath)) != 0) { | |
217 | dprintf("Invalid path to bus: %s\n", buspath); | |
070f5422 GKH |
218 | return NULL; |
219 | } | |
5d4754f1 DS |
220 | bus = alloc_bus(); |
221 | if (bus == NULL) { | |
222 | dprintf("calloc failed\n"); | |
070f5422 GKH |
223 | return NULL; |
224 | } | |
5d4754f1 DS |
225 | strcpy(bus->name, name); |
226 | strcpy(bus->path, buspath); | |
616a7078 AM |
227 | if ((sysfs_remove_trailing_slash(bus->path)) != 0) { |
228 | dprintf("Incorrect path to bus %s\n", bus->path); | |
229 | sysfs_close_bus(bus); | |
230 | return NULL; | |
231 | } | |
070f5422 GKH |
232 | |
233 | return bus; | |
234 | } | |
fe3fe3b2 DS |
235 | |
236 | /** | |
237 | * sysfs_get_bus_device: Get specific device on bus using device's id | |
238 | * @bus: bus to find device on | |
239 | * @id: bus_id for device | |
240 | * returns struct sysfs_device reference or NULL if not found. | |
241 | */ | |
242 | struct sysfs_device *sysfs_get_bus_device(struct sysfs_bus *bus, | |
243 | unsigned char *id) | |
244 | { | |
245 | if (bus == NULL || id == NULL) { | |
246 | errno = EINVAL; | |
247 | return NULL; | |
248 | } | |
249 | ||
5d4754f1 DS |
250 | if (bus->devices == NULL) { |
251 | bus->devices = sysfs_get_bus_devices(bus); | |
252 | if (bus->devices == NULL) | |
253 | return NULL; | |
254 | } | |
255 | ||
fe3fe3b2 DS |
256 | return (struct sysfs_device *)dlist_find_custom(bus->devices, id, |
257 | bus_device_id_equal); | |
258 | } | |
259 | ||
260 | /** | |
261 | * sysfs_get_bus_driver: Get specific driver on bus using driver name | |
262 | * @bus: bus to find driver on | |
263 | * @drvname: name of driver | |
264 | * returns struct sysfs_driver reference or NULL if not found. | |
265 | */ | |
266 | struct sysfs_driver *sysfs_get_bus_driver(struct sysfs_bus *bus, | |
267 | unsigned char *drvname) | |
268 | { | |
269 | if (bus == NULL || drvname == NULL) { | |
270 | errno = EINVAL; | |
271 | return NULL; | |
272 | } | |
273 | ||
5d4754f1 DS |
274 | if (bus->drivers == NULL) { |
275 | bus->drivers = sysfs_get_bus_drivers(bus); | |
276 | if (bus->drivers == NULL) | |
277 | return NULL; | |
278 | } | |
279 | ||
fe3fe3b2 DS |
280 | return (struct sysfs_driver *)dlist_find_custom(bus->drivers, drvname, |
281 | bus_driver_name_equal); | |
282 | } | |
283 | ||
284 | /** | |
285 | * sysfs_get_bus_attributes: returns bus' dlist of attributes | |
286 | * @bus: bus to get attributes for. | |
287 | * returns dlist of attributes or NULL if there aren't any. | |
288 | */ | |
289 | struct dlist *sysfs_get_bus_attributes(struct sysfs_bus *bus) | |
290 | { | |
5d4754f1 | 291 | if (bus == NULL) |
fe3fe3b2 | 292 | return NULL; |
5d4754f1 DS |
293 | |
294 | if (bus->directory == NULL) { | |
295 | bus->directory = sysfs_open_directory(bus->path); | |
296 | if (bus->directory == NULL) | |
297 | return NULL; | |
298 | } | |
299 | if (bus->directory->attributes == NULL) { | |
300 | if ((sysfs_read_dir_attributes(bus->directory)) != 0) | |
301 | return NULL; | |
5d4754f1 | 302 | } |
fe3fe3b2 DS |
303 | return bus->directory->attributes; |
304 | } | |
305 | ||
616a7078 AM |
306 | /** |
307 | * sysfs_refresh_bus_attributes: refreshes the bus's list of attributes | |
308 | * @bus: sysfs_bus whose attributes to refresh | |
309 | * | |
310 | * NOTE: Upon return, prior references to sysfs_attributes for this bus | |
311 | * _may_ not be valid | |
312 | * | |
313 | * Returns list of attributes on success and NULL on failure | |
314 | */ | |
315 | struct dlist *sysfs_refresh_bus_attributes(struct sysfs_bus *bus) | |
316 | { | |
317 | if (bus == NULL) { | |
318 | errno = EINVAL; | |
319 | return NULL; | |
320 | } | |
321 | ||
322 | if (bus->directory == NULL) | |
323 | return (sysfs_get_bus_attributes(bus)); | |
324 | ||
325 | if ((sysfs_refresh_dir_attributes(bus->directory)) != 0) { | |
326 | dprintf("Error refreshing bus attributes\n"); | |
327 | return NULL; | |
328 | } | |
329 | ||
330 | return (bus->directory->attributes); | |
331 | } | |
332 | ||
fe3fe3b2 DS |
333 | /** |
334 | * sysfs_get_bus_attribute: gets a specific bus attribute, if buses had | |
335 | * attributes. | |
336 | * @bus: bus to retrieve attribute from | |
337 | * @attrname: attribute name to retrieve | |
338 | * returns reference to sysfs_attribute if found or NULL if not found | |
339 | */ | |
340 | struct sysfs_attribute *sysfs_get_bus_attribute(struct sysfs_bus *bus, | |
341 | unsigned char *attrname) | |
342 | { | |
5d4754f1 DS |
343 | struct dlist *attrlist = NULL; |
344 | ||
345 | if (bus == NULL) { | |
fe3fe3b2 DS |
346 | errno = EINVAL; |
347 | return NULL; | |
348 | } | |
5d4754f1 DS |
349 | attrlist = sysfs_get_bus_attributes(bus); |
350 | if (attrlist == NULL) | |
351 | return NULL; | |
352 | ||
fe3fe3b2 DS |
353 | return sysfs_get_directory_attribute(bus->directory, attrname); |
354 | } | |
355 | ||
356 | /** | |
357 | * sysfs_open_bus_device: locates a device on a bus and returns it. Device | |
358 | * must be closed using sysfs_close_device. | |
359 | * @busname: Name of bus to search | |
360 | * @dev_id: Id of device on bus. | |
361 | * returns sysfs_device if found or NULL if not. | |
362 | */ | |
363 | struct sysfs_device *sysfs_open_bus_device(unsigned char *busname, | |
364 | unsigned char *dev_id) | |
365 | { | |
366 | struct sysfs_device *rdev = NULL; | |
367 | char path[SYSFS_PATH_MAX]; | |
368 | ||
369 | if (busname == NULL || dev_id == NULL) { | |
370 | errno = EINVAL; | |
371 | return NULL; | |
372 | } | |
373 | ||
374 | memset(path, 0, SYSFS_PATH_MAX); | |
375 | if (sysfs_get_mnt_path(path, SYSFS_PATH_MAX) != 0) { | |
376 | dprintf("Error getting sysfs mount point\n"); | |
377 | return NULL; | |
378 | } | |
bcbe2d8e | 379 | |
616a7078 | 380 | strcat(path, "/"); |
edcd3364 | 381 | strcat(path, SYSFS_BUS_NAME); |
fe3fe3b2 DS |
382 | strcat(path, "/"); |
383 | strcat(path, busname); | |
edcd3364 DS |
384 | strcat(path, "/"); |
385 | strcat(path, SYSFS_DEVICES_NAME); | |
fe3fe3b2 DS |
386 | strcat(path, "/"); |
387 | strcat(path, dev_id); | |
388 | ||
bcbe2d8e | 389 | rdev = sysfs_open_device_path(path); |
fe3fe3b2 DS |
390 | if (rdev == NULL) { |
391 | dprintf("Error getting device %s on bus %s\n", | |
392 | dev_id, busname); | |
393 | return NULL; | |
394 | } | |
395 | ||
396 | return rdev; | |
397 | } | |
398 | ||
fe3fe3b2 DS |
399 | /** |
400 | * sysfs_find_driver_bus: locates the bus the driver is on. | |
401 | * @driver: name of the driver to locate | |
402 | * @busname: buffer to copy name to | |
403 | * @bsize: buffer size | |
404 | * returns 0 with success, -1 with error | |
405 | */ | |
406 | int sysfs_find_driver_bus(const unsigned char *driver, unsigned char *busname, | |
407 | size_t bsize) | |
408 | { | |
409 | unsigned char subsys[SYSFS_PATH_MAX], *bus = NULL, *curdrv = NULL; | |
410 | struct dlist *buslist = NULL, *drivers = NULL; | |
411 | ||
412 | if (driver == NULL || busname == NULL) { | |
413 | errno = EINVAL; | |
414 | return -1; | |
415 | } | |
416 | ||
417 | memset(subsys, 0, SYSFS_PATH_MAX); | |
5d4754f1 | 418 | strcat(subsys, "/"); |
edcd3364 | 419 | strcpy(subsys, SYSFS_BUS_NAME); |
fe3fe3b2 DS |
420 | buslist = sysfs_open_subsystem_list(subsys); |
421 | if (buslist != NULL) { | |
422 | dlist_for_each_data(buslist, bus, char) { | |
423 | memset(subsys, 0, SYSFS_PATH_MAX); | |
edcd3364 DS |
424 | strcat(subsys, "/"); |
425 | strcpy(subsys, SYSFS_BUS_NAME); | |
fe3fe3b2 DS |
426 | strcat(subsys, "/"); |
427 | strcat(subsys, bus); | |
edcd3364 DS |
428 | strcat(subsys, "/"); |
429 | strcat(subsys, SYSFS_DRIVERS_NAME); | |
fe3fe3b2 DS |
430 | drivers = sysfs_open_subsystem_list(subsys); |
431 | if (drivers != NULL) { | |
432 | dlist_for_each_data(drivers, curdrv, char) { | |
433 | if (strcmp(driver, curdrv) == 0) { | |
434 | strncpy(busname, bus, bsize); | |
435 | sysfs_close_list(drivers); | |
436 | sysfs_close_list(buslist); | |
437 | return 0; | |
438 | } | |
439 | } | |
440 | sysfs_close_list(drivers); | |
441 | } | |
442 | } | |
443 | sysfs_close_list(buslist); | |
444 | } | |
445 | return -1; | |
446 | } |