diff options
Diffstat (limited to 'libsysfs/sysfs_device.c')
-rw-r--r-- | libsysfs/sysfs_device.c | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/libsysfs/sysfs_device.c b/libsysfs/sysfs_device.c new file mode 100644 index 0000000000..185b5cf378 --- /dev/null +++ b/libsysfs/sysfs_device.c @@ -0,0 +1,199 @@ +/* + * sysfs_device.c + * + * Generic device utility functions for libsysfs + * + * Copyright (C) 2003 International Business Machines, Inc. + * + * 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" + +/** + * sysfs_close_device: closes and cleans up a device + * @dev = device to clean up + */ +void sysfs_close_device(struct sysfs_device *dev) +{ + if (dev != NULL) { + dev->next = NULL; + dev->driver = NULL; + if (dev->directory != NULL) + sysfs_close_directory(dev->directory); + dev->children = NULL; + free(dev); + } +} + +/** + * alloc_device: allocates and initializes device structure + * returns struct sysfs_device + */ +static struct sysfs_device *alloc_device(void) +{ + return (struct sysfs_device *)calloc(1, sizeof(struct sysfs_device)); +} + +/** + * sysfs_get_device_attr: searches dev's attributes by name + * @dev: device to look through + * @name: attribute name to get + * returns sysfs_attribute reference with success or NULL with error. + */ +struct sysfs_attribute *sysfs_get_device_attr(struct sysfs_device *dev, + const char *name) +{ + struct sysfs_attribute *cur = NULL; + char attrname[SYSFS_NAME_LEN]; + + if (dev == NULL || dev->directory == NULL || name == NULL) { + errno = EINVAL; + return NULL; + } + for (cur = dev->directory->attributes; cur != NULL; cur = cur->next) { + if ((sysfs_get_name_from_path(cur->path, attrname, + SYSFS_NAME_LEN)) != 0) + continue; + if (strcmp(name, attrname) != 0) + continue; + + return cur; + } + + return NULL; +} + +/** + * sysfs_open_device: opens and populates device structure + * @path: path to device, this is the /sys/devices/ path + * returns sysfs_device structure with success or NULL with error + */ +struct sysfs_device *sysfs_open_device(const char *path) +{ + struct sysfs_device *dev = NULL; + struct sysfs_directory *sdir = NULL; + char *p = NULL; + + if (path == NULL) { + errno = EINVAL; + return NULL; + } + dev = alloc_device(); + if (dev == NULL) { + dprintf(stderr, "Error allocating device at %s\n", path); + return NULL; + } + sdir = sysfs_open_directory(path); + if (sdir == NULL) { + dprintf(stderr, "Invalid device at %s\n", path); + errno = EINVAL; + sysfs_close_device(dev); + return NULL; + } + if ((sysfs_read_directory(sdir)) != 0) { + dprintf(stderr, "Error reading device directory at %s\n", path); + sysfs_close_directory(sdir); + sysfs_close_device(dev); + return NULL; + } + dev->directory = sdir; + sysfs_get_name_from_path(sdir->path, dev->bus_id, SYSFS_NAME_LEN); + /* get device name */ + p = sysfs_get_value_from_attributes(sdir->attributes, + SYSFS_NAME_ATTRIBUTE); + if (p != NULL) { + strncpy(dev->name, p, SYSFS_NAME_LEN); + p = dev->name + strlen(dev->name) - 1; + if ((strlen(dev->name) > 0) && *p == '\n') + *p = '\0'; + } + + return dev; +} + +/** + * sysfs_close_device_tree: closes every device in the supplied tree, + * closing children only. + * @devroot: device root of tree. + */ +void sysfs_close_device_tree(struct sysfs_device *devroot) +{ + if (devroot != NULL) { + if (devroot->children != NULL) { + struct sysfs_device *child = NULL, *next = NULL; + + for (child = devroot->children; child != NULL; + child = next) { + next = child->next; + sysfs_close_device_tree(child); + } + } + sysfs_close_device(devroot); + } +} + +/** + * add_device_child_to_parent: adds child device to parent + * @parent: parent device. + * @child: child device to add. + */ +static void add_device_child_to_parent(struct sysfs_device *parent, + struct sysfs_device *child) +{ + if (parent != NULL && child != NULL) { + child->next = parent->children; + parent->children = child; + child->parent = parent; + } +} + +/** + * sysfs_open_device_tree: opens root device and all of its children, + * creating a tree of devices. Only opens children. + * @path: sysfs path to devices + * returns struct sysfs_device and its children with success or NULL with + * error. + */ +struct sysfs_device *sysfs_open_device_tree(const char *path) +{ + struct sysfs_device *rootdev = NULL, *new = NULL; + struct sysfs_directory *cur = NULL; + + if (path == NULL) { + errno = EINVAL; + return NULL; + } + rootdev = sysfs_open_device(path); + if (rootdev == NULL) { + dprintf(stderr, "Error opening root device at %s\n", path); + return NULL; + } + cur = rootdev->directory->subdirs; + while (cur != NULL) { + new = sysfs_open_device_tree(cur->path); + if (new == NULL) { + dprintf(stderr, "Error opening device tree at %s\n", + cur->path); + sysfs_close_device_tree(rootdev); + return NULL; + } + add_device_child_to_parent(rootdev, new); + cur = cur->next; + } + + return rootdev; +} |