| /* |
| * sysfs_bus.c |
| * |
| * Generic bus utility functions for libsysfs |
| * |
| * Copyright (C) IBM Corp. 2003-2005 |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| * |
| */ |
| #include "libsysfs.h" |
| #include "sysfs.h" |
| |
| static void sysfs_close_dev(void *dev) |
| { |
| sysfs_close_device((struct sysfs_device *)dev); |
| } |
| |
| static void sysfs_close_drv(void *drv) |
| { |
| sysfs_close_driver((struct sysfs_driver *)drv); |
| } |
| |
| /* |
| * compares names. |
| * @a: name looked for |
| * @b: sysfs_device comparing being compared |
| * returns 1 if a==b->name or 0 not equal |
| */ |
| static int name_equal(void *a, void *b) |
| { |
| if (!a || !b) |
| return 0; |
| |
| if (strcmp(((char *)a), ((struct sysfs_device *)b)->name) == 0) |
| return 1; |
| |
| return 0; |
| } |
| |
| /** |
| * sysfs_close_bus: close single bus |
| * @bus: bus structure |
| */ |
| void sysfs_close_bus(struct sysfs_bus *bus) |
| { |
| if (bus) { |
| if (bus->attrlist) |
| dlist_destroy(bus->attrlist); |
| if (bus->devices) |
| dlist_destroy(bus->devices); |
| if (bus->drivers) |
| dlist_destroy(bus->drivers); |
| free(bus); |
| } |
| } |
| |
| /** |
| * alloc_bus: mallocs new bus structure |
| * returns sysfs_bus_bus struct or NULL |
| */ |
| static struct sysfs_bus *alloc_bus(void) |
| { |
| return (struct sysfs_bus *)calloc(1, sizeof(struct sysfs_bus)); |
| } |
| |
| /** |
| * sysfs_get_bus_devices: gets all devices for bus |
| * @bus: bus to get devices for |
| * returns dlist of devices with success and NULL with failure |
| */ |
| struct dlist *sysfs_get_bus_devices(struct sysfs_bus *bus) |
| { |
| struct sysfs_device *dev; |
| struct dlist *linklist; |
| char path[SYSFS_PATH_MAX], devpath[SYSFS_PATH_MAX]; |
| char target[SYSFS_PATH_MAX]; |
| char *curlink; |
| |
| if (!bus) { |
| errno = EINVAL; |
| return NULL; |
| } |
| memset(path, 0, SYSFS_PATH_MAX); |
| safestrcpy(path, bus->path); |
| safestrcat(path, "/"); |
| safestrcat(path, SYSFS_DEVICES_NAME); |
| |
| linklist = read_dir_links(path); |
| if (linklist) { |
| dlist_for_each_data(linklist, curlink, char) { |
| if (bus->devices) { |
| dev = (struct sysfs_device *) |
| dlist_find_custom(bus->devices, |
| (void *)curlink, name_equal); |
| if (dev) |
| continue; |
| } |
| safestrcpy(devpath, path); |
| safestrcat(devpath, "/"); |
| safestrcat(devpath, curlink); |
| if (sysfs_get_link(devpath, target, SYSFS_PATH_MAX)) { |
| dprintf("Error getting link - %s\n", devpath); |
| continue; |
| } |
| dev = sysfs_open_device_path(target); |
| if (!dev) { |
| dprintf("Error opening device at %s\n", |
| target); |
| continue; |
| } |
| if (!bus->devices) |
| bus->devices = dlist_new_with_delete |
| (sizeof(struct sysfs_device), |
| sysfs_close_dev); |
| dlist_unshift_sorted(bus->devices, dev, sort_list); |
| } |
| sysfs_close_list(linklist); |
| } |
| return (bus->devices); |
| } |
| |
| /** |
| * sysfs_get_bus_drivers: gets all drivers for bus |
| * @bus: bus to get devices for |
| * returns dlist of devices with success and NULL with failure |
| */ |
| struct dlist *sysfs_get_bus_drivers(struct sysfs_bus *bus) |
| { |
| struct sysfs_driver *drv; |
| struct dlist *dirlist; |
| char path[SYSFS_PATH_MAX], drvpath[SYSFS_PATH_MAX]; |
| char *curdir; |
| |
| if (!bus) { |
| errno = EINVAL; |
| return NULL; |
| } |
| memset(path, 0, SYSFS_PATH_MAX); |
| safestrcpy(path, bus->path); |
| safestrcat(path, "/"); |
| safestrcat(path, SYSFS_DRIVERS_NAME); |
| |
| dirlist = read_dir_subdirs(path); |
| if (dirlist) { |
| dlist_for_each_data(dirlist, curdir, char) { |
| if (bus->drivers) { |
| drv = (struct sysfs_driver *) |
| dlist_find_custom(bus->drivers, |
| (void *)curdir, name_equal); |
| if (drv) |
| continue; |
| } |
| safestrcpy(drvpath, path); |
| safestrcat(drvpath, "/"); |
| safestrcat(drvpath, curdir); |
| drv = sysfs_open_driver_path(drvpath); |
| if (!drv) { |
| dprintf("Error opening driver at %s\n", |
| drvpath); |
| continue; |
| } |
| if (!bus->drivers) |
| bus->drivers = dlist_new_with_delete |
| (sizeof(struct sysfs_driver), |
| sysfs_close_drv); |
| dlist_unshift_sorted(bus->drivers, drv, sort_list); |
| } |
| sysfs_close_list(dirlist); |
| } |
| return (bus->drivers); |
| } |
| |
| /** |
| * sysfs_open_bus: opens specific bus and all its devices on system |
| * returns sysfs_bus structure with success or NULL with error. |
| */ |
| struct sysfs_bus *sysfs_open_bus(const char *name) |
| { |
| struct sysfs_bus *bus; |
| char buspath[SYSFS_PATH_MAX]; |
| |
| if (!name) { |
| errno = EINVAL; |
| return NULL; |
| } |
| |
| memset(buspath, 0, SYSFS_PATH_MAX); |
| if (sysfs_get_mnt_path(buspath, SYSFS_PATH_MAX)) { |
| dprintf("Sysfs not supported on this system\n"); |
| return NULL; |
| } |
| |
| safestrcat(buspath, "/"); |
| safestrcat(buspath, SYSFS_BUS_NAME); |
| safestrcat(buspath, "/"); |
| safestrcat(buspath, name); |
| if (sysfs_path_is_dir(buspath)) { |
| dprintf("Invalid path to bus: %s\n", buspath); |
| return NULL; |
| } |
| bus = alloc_bus(); |
| if (!bus) { |
| dprintf("calloc failed\n"); |
| return NULL; |
| } |
| safestrcpy(bus->name, name); |
| safestrcpy(bus->path, buspath); |
| if (sysfs_remove_trailing_slash(bus->path)) { |
| dprintf("Incorrect path to bus %s\n", bus->path); |
| sysfs_close_bus(bus); |
| return NULL; |
| } |
| |
| return bus; |
| } |
| |
| /** |
| * sysfs_get_bus_device: Get specific device on bus using device's id |
| * @bus: bus to find device on |
| * @id: bus_id for device |
| * returns struct sysfs_device reference or NULL if not found. |
| */ |
| struct sysfs_device *sysfs_get_bus_device(struct sysfs_bus *bus, |
| const char *id) |
| { |
| struct sysfs_device *dev = NULL; |
| char devpath[SYSFS_PATH_MAX], target[SYSFS_PATH_MAX]; |
| |
| if (!bus || !id) { |
| errno = EINVAL; |
| return NULL; |
| } |
| |
| if (bus->devices) { |
| dev = (struct sysfs_device *)dlist_find_custom |
| (bus->devices, (void *)id, name_equal); |
| if (dev) |
| return dev; |
| } |
| safestrcpy(devpath, bus->path); |
| safestrcat(devpath, "/"); |
| safestrcat(devpath, SYSFS_DEVICES_NAME); |
| safestrcat(devpath, "/"); |
| safestrcat(devpath, id); |
| if (sysfs_path_is_link(devpath)) { |
| dprintf("No such device %s on bus %s?\n", id, bus->name); |
| return NULL; |
| } |
| if (!sysfs_get_link(devpath, target, SYSFS_PATH_MAX)) { |
| dev = sysfs_open_device_path(target); |
| if (!dev) { |
| dprintf("Error opening device at %s\n", target); |
| return NULL; |
| } |
| if (!bus->devices) |
| bus->devices = dlist_new_with_delete |
| (sizeof(struct sysfs_device), |
| sysfs_close_dev); |
| dlist_unshift_sorted(bus->devices, dev, sort_list); |
| } |
| return dev; |
| } |
| |
| /** |
| * sysfs_get_bus_driver: Get specific driver on bus using driver name |
| * @bus: bus to find driver on |
| * @drvname: name of driver |
| * returns struct sysfs_driver reference or NULL if not found. |
| */ |
| struct sysfs_driver *sysfs_get_bus_driver(struct sysfs_bus *bus, |
| const char *drvname) |
| { |
| struct sysfs_driver *drv; |
| char drvpath[SYSFS_PATH_MAX]; |
| |
| if (!bus || !drvname) { |
| errno = EINVAL; |
| return NULL; |
| } |
| |
| if (bus->drivers) { |
| drv = (struct sysfs_driver *)dlist_find_custom |
| (bus->drivers, (void *)drvname, name_equal); |
| if (drv) |
| return drv; |
| } |
| safestrcpy(drvpath, bus->path); |
| safestrcat(drvpath, "/"); |
| safestrcat(drvpath, SYSFS_DRIVERS_NAME); |
| safestrcat(drvpath, "/"); |
| safestrcat(drvpath, drvname); |
| drv = sysfs_open_driver_path(drvpath); |
| if (!drv) { |
| dprintf("Error opening driver at %s\n", drvpath); |
| return NULL; |
| } |
| if (!bus->drivers) |
| bus->drivers = dlist_new_with_delete |
| (sizeof(struct sysfs_driver), |
| sysfs_close_drv); |
| dlist_unshift_sorted(bus->drivers, drv, sort_list); |
| return drv; |
| } |
| |