chiark / gitweb /
19fc275d842e30ecbde602ca620b35ce0e84d911
[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
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 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
69 /**
70  * sysfs_close_bus: close single bus
71  * @bus: bus structure
72  */
73 void sysfs_close_bus(struct sysfs_bus *bus)
74 {
75         if (bus != NULL) {
76                 if (bus->directory != NULL)
77                         sysfs_close_directory(bus->directory);
78                 if (bus->devices)
79                         dlist_destroy(bus->devices);
80                 if (bus->drivers)
81                         dlist_destroy(bus->drivers);
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 /**
96  * open_bus_dir: opens up sysfs bus directory
97  * returns sysfs_directory struct with success and NULL with error
98  */
99 static struct sysfs_directory *open_bus_dir(const unsigned char *name)
100 {
101         struct sysfs_directory *busdir = NULL;
102         unsigned char buspath[SYSFS_PATH_MAX];
103
104         if (name == NULL) {
105                 errno = EINVAL;
106                 return NULL;
107         }
108
109         memset(buspath, 0, SYSFS_PATH_MAX);
110         if ((sysfs_get_mnt_path(buspath, SYSFS_PATH_MAX)) != 0) {
111                 dprintf("Sysfs not supported on this system\n");
112                 return NULL;
113         }
114
115         strcat(buspath, SYSFS_BUS_DIR);
116         strcat(buspath, "/");
117         strcat(buspath, name);
118         busdir = sysfs_open_directory(buspath);
119         if (busdir == NULL) {
120                 errno = EINVAL;
121                 dprintf("Bus %s not supported on this system\n",
122                         name);
123                 return NULL;
124         }
125         if ((sysfs_read_directory(busdir)) != 0) {
126                 dprintf("Error reading %s bus dir %s\n", name, 
127                         buspath);
128                 sysfs_close_directory(busdir);
129                 return NULL;
130         }
131         /* read in devices and drivers subdirs */
132         sysfs_read_all_subdirs(busdir);
133
134         return busdir;
135 }
136
137 /**
138  * get_all_bus_devices: gets all devices for bus
139  * @bus: bus to get devices for
140  * returns 0 with success and -1 with failure
141  */
142 static int get_all_bus_devices(struct sysfs_bus *bus)
143 {
144         struct sysfs_device *bdev = NULL;
145         struct sysfs_directory *cur = NULL;
146         struct sysfs_link *curl = NULL;
147
148         if (bus == NULL || bus->directory == NULL) {
149                 errno = EINVAL;
150                 return -1;
151         }
152         if (bus->directory->subdirs == NULL)
153                 return 0;
154
155         dlist_for_each_data(bus->directory->subdirs, cur, 
156                         struct sysfs_directory) {
157                 if (strcmp(cur->name, SYSFS_DEVICES_NAME) != 0)
158                         continue;
159                 if (cur->links == NULL)
160                         continue;
161                 dlist_for_each_data(cur->links, curl, struct sysfs_link) {
162                         bdev = sysfs_open_device(curl->target);
163                         if (bdev == NULL) {
164                                 dprintf("Error opening device at %s\n",
165                                         curl->target);
166                                 continue;
167                         }
168                         if (bus->devices == NULL)
169                                 bus->devices = dlist_new_with_delete
170                                         (sizeof(struct sysfs_device),
171                                                         sysfs_close_dev);
172                         dlist_unshift(bus->devices, bdev);
173                 }
174         }
175                         
176         return 0;
177 }
178
179 /**
180  * get_all_bus_drivers: get all pci drivers
181  * @bus: pci bus to add drivers to
182  * returns 0 with success and -1 with error
183  */
184 static int get_all_bus_drivers(struct sysfs_bus *bus)
185 {
186         struct sysfs_driver *driver = NULL;
187         struct sysfs_directory *cur = NULL;
188         struct sysfs_directory *cursub = NULL;
189
190         if (bus == NULL || bus->directory == NULL) {
191                 errno = EINVAL;
192                 return -1;
193         }
194         if (bus->directory->subdirs == NULL)
195                 return 0;
196
197         dlist_for_each_data(bus->directory->subdirs, cur,
198                         struct sysfs_directory) {
199                 if (strcmp(cur->name, SYSFS_DRIVERS_NAME) != 0)
200                         continue;
201                 if (cur->subdirs == NULL)
202                         continue;
203                 dlist_for_each_data(cur->subdirs, cursub,
204                                 struct sysfs_directory) {
205                         driver = sysfs_open_driver(cursub->path);
206                         if (driver == NULL) {
207                                 dprintf("Error opening driver at %s\n",
208                                         cursub->path);
209                                 continue;
210                         }
211                         if (bus->drivers == NULL)
212                                 bus->drivers = dlist_new_with_delete
213                                         (sizeof(struct sysfs_driver),
214                                                         sysfs_close_drv);
215                         dlist_unshift(bus->drivers, driver);
216                 }
217         }
218         
219         return 0;
220 }
221
222 /**
223  * match_bus_device_to_driver: returns 1 if device is bound to driver
224  * @driver: driver to match
225  * @busid: busid of device to match
226  * returns 1 if found and 0 if not found
227  */
228 static int match_bus_device_to_driver(struct sysfs_driver *driver, 
229                                                         unsigned char *busid)
230 {
231         struct sysfs_link *cur = NULL;
232         int found = 0;
233
234         if (driver == NULL || driver->directory == NULL || busid == NULL) {
235                 errno = EINVAL;
236                 return found;
237         }
238         if (driver->directory->links != NULL) {
239                 dlist_for_each_data(driver->directory->links, cur,
240                                 struct sysfs_link) {
241                         if ((strcmp(cur->name, busid)) == 0)
242                                 found++;
243                 }
244         }
245         return found;
246 }
247
248 /**
249  * link_bus_devices_to_drivers: goes through and links devices to drivers
250  * @bus: bus to link
251  */
252 static void link_bus_devices_to_drivers(struct sysfs_bus *bus)
253 {
254         struct sysfs_device *dev = NULL;
255         struct sysfs_driver *drv = NULL;
256         
257         if (bus != NULL && bus->devices != NULL && bus->drivers != NULL) {
258                 dlist_for_each_data(bus->devices, dev, struct sysfs_device) {
259                         dlist_for_each_data(bus->drivers, drv,
260                                         struct sysfs_driver) {
261                                 if ((match_bus_device_to_driver(drv, 
262                                                 dev->bus_id)) != 0) {
263                                         strncpy(dev->driver_name, drv->name,
264                                                         SYSFS_NAME_LEN);
265                                         if (drv->devices == NULL)
266                                                 drv->devices = dlist_new
267                                                         (sizeof(struct 
268                                                                 sysfs_device));
269                                         dlist_unshift(drv->devices, dev);
270                                 }
271                         }
272                 }
273         }
274 }
275
276 /**
277  * sysfs_open_bus: opens specific bus and all its devices on system
278  * returns sysfs_bus structure with success or NULL with error.
279  */
280 struct sysfs_bus *sysfs_open_bus(const unsigned char *name)
281 {
282         struct sysfs_bus *bus = NULL;
283         struct sysfs_directory *busdir = NULL;
284
285         if (name == NULL) {
286                 errno = EINVAL;
287                 return NULL;
288         }
289
290         bus = alloc_bus();
291         if (bus == NULL) {
292                 dprintf("calloc failed\n");
293                 return NULL;
294         }
295         strcpy(bus->name, name);        
296         busdir = open_bus_dir(name);
297         if (busdir == NULL) {
298                 dprintf("Invalid bus, %s not supported on this system\n",
299                         name);
300                 sysfs_close_bus(bus);
301                 return NULL;
302         }
303         strcpy(bus->path, busdir->path);
304         bus->directory = busdir;
305         if ((get_all_bus_devices(bus)) != 0) {
306                 dprintf("Error reading %s bus devices\n", name);
307                 sysfs_close_bus(bus);
308                 return NULL;
309         }
310         if ((get_all_bus_drivers(bus)) != 0) {
311                 dprintf("Error reading %s bus drivers\n", name);
312                 sysfs_close_bus(bus);
313                 return NULL;
314         }
315         link_bus_devices_to_drivers(bus);
316
317         return bus;
318 }
319
320 /**
321  * sysfs_get_bus_device: Get specific device on bus using device's id
322  * @bus: bus to find device on
323  * @id: bus_id for device
324  * returns struct sysfs_device reference or NULL if not found.
325  */
326 struct sysfs_device *sysfs_get_bus_device(struct sysfs_bus *bus, 
327                                                         unsigned char *id)
328 {
329         if (bus == NULL || id == NULL) {
330                 errno = EINVAL;
331                 return NULL;
332         }
333
334         return (struct sysfs_device *)dlist_find_custom(bus->devices, id,
335                 bus_device_id_equal);
336 }
337
338 /**
339  * sysfs_get_bus_driver: Get specific driver on bus using driver name
340  * @bus: bus to find driver on
341  * @drvname: name of driver
342  * returns struct sysfs_driver reference or NULL if not found.
343  */
344 struct sysfs_driver *sysfs_get_bus_driver(struct sysfs_bus *bus, 
345                                                         unsigned char *drvname)
346 {
347         if (bus == NULL || drvname == NULL) {
348                 errno = EINVAL;
349                 return NULL;
350         }
351
352         return (struct sysfs_driver *)dlist_find_custom(bus->drivers, drvname,
353                 bus_driver_name_equal);
354 }
355
356 /**
357  * sysfs_get_bus_attributes: returns bus' dlist of attributes
358  * @bus: bus to get attributes for.
359  * returns dlist of attributes or NULL if there aren't any.
360  */
361 struct dlist *sysfs_get_bus_attributes(struct sysfs_bus *bus)
362 {
363         if (bus == NULL || bus->directory == NULL)
364                 return NULL;
365         return bus->directory->attributes;
366 }
367
368 /**
369  * sysfs_get_bus_attribute: gets a specific bus attribute, if buses had
370  *      attributes.
371  * @bus: bus to retrieve attribute from
372  * @attrname: attribute name to retrieve
373  * returns reference to sysfs_attribute if found or NULL if not found
374  */
375 struct sysfs_attribute *sysfs_get_bus_attribute(struct sysfs_bus *bus,
376                                                 unsigned char *attrname)
377 {
378         if (bus == NULL || bus->directory == NULL || attrname == NULL) {
379                 errno = EINVAL;
380                 return NULL;
381         }
382         return sysfs_get_directory_attribute(bus->directory, attrname);
383 }
384
385 /**
386  * sysfs_open_bus_device: locates a device on a bus and returns it. Device
387  *      must be closed using sysfs_close_device.
388  * @busname: Name of bus to search
389  * @dev_id: Id of device on bus.
390  * returns sysfs_device if found or NULL if not.
391  */
392 struct sysfs_device *sysfs_open_bus_device(unsigned char *busname, 
393                                                         unsigned char *dev_id)
394 {
395         struct sysfs_device *rdev = NULL;
396         char path[SYSFS_PATH_MAX];
397
398         if (busname == NULL || dev_id == NULL) {
399                 errno = EINVAL;
400                 return NULL;
401         }
402
403         memset(path, 0, SYSFS_PATH_MAX);
404         if (sysfs_get_mnt_path(path, SYSFS_PATH_MAX) != 0) {
405                 dprintf("Error getting sysfs mount point\n");
406                 return NULL;
407         }
408
409         strcat(path, SYSFS_BUS_DIR);
410         strcat(path, "/");
411         strcat(path, busname);
412         strcat(path, SYSFS_DEVICES_DIR);
413         strcat(path, "/");
414         strcat(path, dev_id);
415
416         rdev = sysfs_open_device(path);
417         if (rdev == NULL) {
418                 dprintf("Error getting device %s on bus %s\n",
419                                 dev_id, busname);
420                 return NULL;
421         }
422         
423         return rdev;
424 }
425
426 /**
427  * sysfs_find_device_bus: locates the bus a device is on.
428  * @dev_id: device id.
429  * @busname: buffer to copy name to
430  * @bsize: buffer size
431  * returns 0 with success or -1 with error
432  */
433 int sysfs_find_device_bus(const unsigned char *dev_id, unsigned char *busname, 
434                                                                 size_t bsize)
435 {
436         unsigned char subsys[SYSFS_NAME_LEN], *bus = NULL, *curdev = NULL; 
437         struct dlist *buslist = NULL, *device_list = NULL;
438
439         if (dev_id == NULL || busname == NULL) {
440                 errno = EINVAL;
441                 return -1;
442         }
443         
444         strcpy(subsys, SYSFS_BUS_DIR);  /* subsys = /bus */
445         buslist = sysfs_open_subsystem_list(subsys);
446         if (buslist != NULL) {
447                 dlist_for_each_data(buslist, bus, char) {
448                         device_list = sysfs_open_bus_devices_list(bus);
449                         if (device_list != NULL) {
450                                 dlist_for_each_data(device_list, 
451                                                 curdev, char) {
452                                         if (strcmp(dev_id, curdev) == 0) {
453                                                 strncpy(busname, 
454                                                         bus, bsize);
455                                                 sysfs_close_list(device_list);
456                                                 sysfs_close_list(buslist);
457                                                 return 0;
458                                         }
459                                 }
460                         sysfs_close_list(device_list);
461                         }
462                 }
463                 sysfs_close_list(buslist);
464         }
465         return -1;
466 }
467
468 /**
469  * sysfs_find_driver_bus: locates the bus the driver is on.
470  * @driver: name of the driver to locate
471  * @busname: buffer to copy name to
472  * @bsize: buffer size
473  * returns 0 with success, -1 with error
474  */
475 int sysfs_find_driver_bus(const unsigned char *driver, unsigned char *busname,
476                                                         size_t bsize)
477 {
478         unsigned char subsys[SYSFS_PATH_MAX], *bus = NULL, *curdrv = NULL;
479         struct dlist *buslist = NULL, *drivers = NULL;
480
481         if (driver == NULL || busname == NULL) {
482                 errno = EINVAL;
483                 return -1;
484         }
485
486         memset(subsys, 0, SYSFS_PATH_MAX);
487         strcpy(subsys, SYSFS_BUS_DIR);
488         buslist = sysfs_open_subsystem_list(subsys);
489         if (buslist != NULL) {
490                 dlist_for_each_data(buslist, bus, char) {
491                         memset(subsys, 0, SYSFS_PATH_MAX);
492                         strcpy(subsys, SYSFS_BUS_DIR);
493                         strcat(subsys, "/");
494                         strcat(subsys, bus);
495                         strcat(subsys, SYSFS_DRIVERS_DIR);
496                         drivers = sysfs_open_subsystem_list(subsys);
497                         if (drivers != NULL) {
498                                 dlist_for_each_data(drivers, curdrv, char) {
499                                         if (strcmp(driver, curdrv) == 0) {
500                                                 strncpy(busname, bus, bsize);
501                                                 sysfs_close_list(drivers);
502                                                 sysfs_close_list(buslist);
503                                                 return 0;
504                                         }
505                                 }
506                                 sysfs_close_list(drivers);
507                         }
508                 }
509                 sysfs_close_list(buslist);
510         }
511         return -1;
512 }
513