/* * 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; }