summaryrefslogtreecommitdiff
path: root/libsysfs/sysfs_device.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsysfs/sysfs_device.c')
-rw-r--r--libsysfs/sysfs_device.c199
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;
+}