[PATCH 2/3] gpiolib: add support for having symlinks under gpio class directory

Jani Nikula
2009-12-09T13:50:08Z

Extend the functionality of gpio_export_link() to allow exported GPIOs
to have names using sysfs links under /sys/class/gpio.

Also automatically remove links on gpio_unexport().

Signed-off-by: Jani Nikula <ext-jani.1.nikula@nokia.com>
---
drivers/gpio/gpiolib.c | 95 +++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 90 insertions(+), 5 deletions(-)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 50de0f5..f254195 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -9,6 +9,8 @@
#include <linux/seq_file.h>
#include <linux/gpio.h>
#include <linux/idr.h>
+#include <linux/list.h>
+#include "../base/base.h"

/* Optional implementation infrastructure for GPIO interfaces.
@@ -608,6 +610,84 @@ static struct class gpio_class = {
.class_attrs = gpio_class_attrs,
};

+/* sysfs link */
+struct sysfs_link {
+ unsigned gpio;
+ struct device *dev;
+ const char *name;
+ struct list_head node;
+};
+
+/* list of links to gpio sysfs nodes, protected by sysfs_mutex */
+static LIST_HEAD(sysfs_links);
+
+/* create link, store info */
+static int create_link(unsigned gpio, struct device *dev,
+ struct kobject *target, const char *name)
+{
+ struct sysfs_link *link;
+ int status = -ENOMEM;
+
+ link = kzalloc(sizeof(*link), GFP_KERNEL);
+ if (link == NULL)
+ goto err0;
+
+ link->name = kstrdup(name, GFP_KERNEL);
+ if (link->name == NULL)
+ goto err1;
+
+ link->gpio = gpio;
+ link->dev = dev;
+
+ if (dev != NULL) {
+ dev = get_device(dev);
+ if (dev == NULL) {
+ status = -ENODEV;
+ goto err2;
+ }
+ status = sysfs_create_link(&dev->kobj, target, name);
+ } else {
+ status = class_create_link(&gpio_class, target, name);
+ }
+
+ if (status)
+ goto err3;
+
+ list_add(&link->node, &sysfs_links);
+
+ return 0;
+
+err3:
+ if (dev != NULL)
+ put_device(dev);
+err2:
+ kfree(link->name);
+err1:
+ kfree(link);
+err0:
+
+ return status;
+}
+
+/* remove symlinks pointing to gpio */
+static void remove_links(unsigned gpio)
+{
+ struct sysfs_link *link, *tmp;
+
+ list_for_each_entry_safe(link, tmp, &sysfs_links, node) {
+ if (link->gpio == gpio) {
+ if (link->dev != NULL) {
+ sysfs_remove_link(&link->dev->kobj, link->name);
+ put_device(link->dev);
+ } else {
+ class_remove_link(&gpio_class, link->name);
+ }
+ list_del(&link->node);
+ kfree(link->name);
+ kfree(link);
+ }
+ }
+}

/**
* gpio_export - export a GPIO through sysfs
@@ -701,12 +781,17 @@ static int match_export(struct device *dev, void *data)

/**
* gpio_export_link - create a sysfs link to an exported GPIO node
- * @dev: device under which to create symlink
+ * @dev: device under which to create symlink, or NULL for gpio class
* @name: name of the symlink
* @gpio: gpio to create symlink to, already exported
*
- * Set up a symlink from /sys/.../dev/name to /sys/class/gpio/gpioN
- * node. Caller is responsible for unlinking.
+ * If @dev is non-NULL, set up a symlink from /sys/.../dev/name to
+ * /sys/class/gpio/gpioN node.
+ *
+ * If @dev is NULL, set up the symlink from /sys/class/gpio/name to
+ * /sys/class/gpio/gpioN node.
+ *
+ * Symlinks are removed on gpio_unexport() on the @gpio.
*
* Returns zero on success, else an error.
*/
@@ -727,8 +812,7 @@ int gpio_export_link(struct device *dev, const char *name, unsigned gpio)

tdev = class_find_device(&gpio_class, NULL, desc, match_export);
if (tdev != NULL) {
- status = sysfs_create_link(&dev->kobj, &tdev->kobj,
- name);
+ status = create_link(gpio, dev, &tdev->kobj, name);
} else {
status = -ENODEV;
}
@@ -769,6 +853,7 @@ void gpio_unexport(unsigned gpio)
if (dev) {
gpio_setup_irq(desc, dev, 0);
clear_bit(FLAG_EXPORT, &desc->flags);
+ remove_links(gpio);
put_device(dev);
device_unregister(dev);
status = 0;
--
1.6.5.2