From: Trent Piepho <tpiepho@freescale.com>
To: David Brownell <david-b@pacbell.net>
Cc: Jean Delvare <khali@linux-fr.org>,
Linux Kernel list <linux-kernel@vger.kernel.org>
Subject: Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver
Date: Thu, 3 Apr 2008 19:06:27 -0700 (PDT) [thread overview]
Message-ID: <Pine.LNX.4.64.0804031844500.11928@t2.domain.actdsltmp> (raw)
In-Reply-To: <200711301259.22666.david-b@pacbell.net>
On Fri, 30 Nov 2007, David Brownell wrote:
> On Friday 30 November 2007, Jean Delvare wrote:
>>
>> So the user-space interface would be part of the generic GPIO
>> infrastructure? I like the idea.
>
> I thought that would make sense too! :) Someone would need to
> write the code though. Having such a mechanism would provide
> another "carrot" to migrate folk towards the gpiolib core.
Here's some code to do this. It's not entirely perfect yet, but it is
usable. I create a directory in sysfs (class/gpio/<name>:<num>) for each
gpio line. The are files in this directory to allow reading/setting the
gpio value and direction. One can also see the label associated with the
gpio if gpiolib is keeping track of labels (i.e. debugfs is on). sysfs
also creates a gpio directory under the device associated with the gpio
lines (this is a new thing one needs to add) with that device's gpios.
Actually, think these are "real" sysfs directories, and the ones in
/sys/class are copies made by sysfs.
----------------------------------------------------------------------------
>From c65d0fb239b79de7f595e47edb2fb641217e7309 Mon Sep 17 00:00:00 2001
From: Trent Piepho <tpiepho@freescale.com>
Date: Thu, 3 Apr 2008 18:37:23 -0700
Subject: GPIO: Create a sysfs gpio class for messing with GPIOs from userspace
struct gpio_chip gets a new field, dev, which points to the struct device
of whatever the gpio lines are part of. If this is non-NULL, gpio class
entries are created for this device when gpiochip_add() is called, by a new
function gpiochip_classdev_register().
It creates a sysfs directory for each gpio the chip defines. Each device
has three attributes so far, direction, value, and label. label is
read-only, and will only be present if DEBUG_FS is on, as without DEBUG_FS
the gpio layer doesn't keep track of any labels.
Maybe the value file should be changed to RO or WO depending on direction?
Setting the direction auto-allocates the gpio, which is not really what I
wanted. Maybe I should call the chip set method directly?
There are almost certainly a bunch of races with gpio_desc access.
No code has been written yet to remove the devices from the class when the
class is removed.
The GPIO_CLASS define/ifdef code should either be removed, or turned into a
Kconfig variable that can be used turn this feature on and off.
Signed-off-by: Trent Piepho <tpiepho@freescale.com>
---
drivers/gpio/gpiolib.c | 188 ++++++++++++++++++++++++++++++++++++++++++++
include/asm-generic/gpio.h | 2 +
2 files changed, 190 insertions(+), 0 deletions(-)
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index d8db2f8..db0677d 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -18,6 +18,7 @@
* only an instruction or two per bit.
*/
+#define GPIO_CLASS 1
/* When debugging, extend minimal trust to callers and platform code.
* Also emit diagnostic messages that may help initial bringup, when
@@ -47,6 +48,9 @@ struct gpio_desc {
#ifdef CONFIG_DEBUG_FS
const char *label;
#endif
+#if GPIO_CLASS
+ struct device *dev;
+#endif
};
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
@@ -57,6 +61,174 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label)
#endif
}
+#if GPIO_CLASS
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/err.h>
+
+static struct class *gpio_class;
+
+static ssize_t gpio_direction_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_desc *gdesc = dev_get_drvdata(dev);
+
+ strcpy(buf, test_bit(FLAG_IS_OUT, &gdesc->flags) ? "out\n" : "in\n\0");
+
+ return 5;
+}
+
+static ssize_t gpio_direction_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ const struct gpio_desc *gdesc = dev_get_drvdata(dev);
+ int d, n = gdesc - gpio_desc;
+
+ if (size >= 3 && !strncmp(buf, "out", 3)) {
+ d = 1;
+ } else if (size >= 2 && !strncmp(buf, "in", 2)) {
+ d = 0;
+ } else {
+ d = simple_strtoul(buf, NULL, 0);
+ }
+
+ if (d)
+ gpio_direction_output(n, 0);
+ else
+ gpio_direction_input(n);
+
+ return size;
+}
+
+static DEVICE_ATTR(direction, 0644, gpio_direction_show, gpio_direction_store);
+
+static ssize_t gpio_value_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_desc *gdesc = dev_get_drvdata(dev);
+ int n = gdesc - gpio_desc;
+
+ if (test_bit(FLAG_IS_OUT, &gdesc->flags)) {
+ return -EINVAL;
+ /* strcpy(buf, "-1\n"); return 4; */ /* Or this? */
+ }
+ return sprintf(buf, "%d\n", gpio_get_value(n)) + 1;
+}
+
+static ssize_t gpio_value_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ const struct gpio_desc *gdesc = dev_get_drvdata(dev);
+ int v, n = gdesc - gpio_desc;
+
+ if (!test_bit(FLAG_IS_OUT, &gdesc->flags))
+ return -EINVAL;
+
+ v = simple_strtoul(buf, NULL, 0);
+ gpio_set_value(n, v);
+
+ return size;
+}
+
+static DEVICE_ATTR(value, 0644, gpio_value_show, gpio_value_store);
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t gpio_label_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_desc *gdesc = dev_get_drvdata(dev);
+
+ if (!gdesc->label) {
+ strcpy(buf, "free\n");
+ return 6;
+ } else
+ return sprintf(buf, "%s\n", gdesc->label) + 1;
+}
+
+static DEVICE_ATTR(label, 0444, gpio_label_show, NULL);
+#endif
+
+static int gpiochip_classdev_register(const struct gpio_chip *chip)
+{
+ int ret, i;
+ struct gpio_desc *gdesc;
+
+ BUG_ON(!chip->dev);
+
+ for (i = chip->base; i < chip->base + chip->ngpio; i++) {
+ gdesc = gpio_desc + i;
+ gdesc->dev = device_create(gpio_class, chip->dev, 0, "%s:%d",
+ chip->label, i);
+ if (IS_ERR(gdesc->dev)) {
+ ret = PTR_ERR(gdesc->dev);
+ i--;
+ goto fail;
+ }
+
+ dev_set_drvdata(gdesc->dev, gdesc);
+
+ ret = device_create_file(gdesc->dev, &dev_attr_direction);
+ if (ret)
+ goto fail_dev;
+ ret = device_create_file(gdesc->dev, &dev_attr_value);
+ if (ret)
+ goto fail_dir;
+#ifdef CONFIG_DEBUG_FS
+ ret = device_create_file(gdesc->dev, &dev_attr_label);
+ if (ret)
+ goto fail_value;
+#endif
+ }
+ return 0;
+
+fail:
+ for (; i >= chip->base; i--) {
+ gdesc = gpio_desc + i;
+
+#ifdef CONFIG_DEBUG_FS
+ device_remove_file(gdesc->dev, &dev_attr_label);
+
+fail_value:
+#endif
+ device_remove_file(gdesc->dev, &dev_attr_value);
+
+fail_dir:
+ device_remove_file(gdesc->dev, &dev_attr_direction);
+
+fail_dev:
+ device_unregister(gdesc->dev);
+ gdesc->dev = NULL;
+ }
+ return ret;
+}
+
+static int __init gpio_class_init(void)
+{
+ gpio_class = class_create(THIS_MODULE, "gpio");
+ if (IS_ERR(gpio_class))
+ return PTR_ERR(gpio_class);
+ return 0;
+}
+
+static void __exit gpio_class_exit(void)
+{
+ /* FIXME: Code to remove all the sysfs devices and files created
+ * should go here */
+ class_destroy(gpio_class);
+}
+subsys_initcall(gpio_class_init);
+module_exit(gpio_class_exit);
+
+#else /* no class */
+
+/* I coulda been a contender, I coulda had class... */
+static inline int gpiochip_classdev_register(const struct gpio_chip *chip)
+{ return 0; }
+
+#endif
+
+
/* Warn when drivers omit gpio_request() calls -- legal but ill-advised
* when setting direction, and otherwise illegal. Until board setup code
* and drivers use explicit requests everywhere (which won't happen when
@@ -118,6 +290,22 @@ int gpiochip_add(struct gpio_chip *chip)
}
spin_unlock_irqrestore(&gpio_lock, flags);
+
+ if (status)
+ goto fail;
+
+ if (chip->dev) {
+ /* can sleep, so can't call with the spinlock held */
+ status = gpiochip_classdev_register(chip);
+ if (status) {
+ /* De-allocate GPIOs */
+ spin_lock_irqsave(&gpio_lock, flags);
+ for (id = chip->base; id < chip->base + chip->ngpio; id++)
+ gpio_desc[id].chip = NULL;
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ }
+ }
+
fail:
/* failures here can mean systems won't boot... */
if (status)
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index f29a502..b2a5262 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -29,6 +29,7 @@ struct seq_file;
* @dbg_show: optional routine to show contents in debugfs; default code
* will be used when this is omitted, but custom code can show extra
* state (such as pullup/pulldown configuration).
+ * @dev: optional device for the GPIO chip. Used to create sysfs files.
* @base: identifies the first GPIO number handled by this chip; or, if
* negative during registration, requests dynamic ID allocation.
* @ngpio: the number of GPIOs handled by this controller; the last GPIO
@@ -59,6 +60,7 @@ struct gpio_chip {
unsigned offset, int value);
void (*dbg_show)(struct seq_file *s,
struct gpio_chip *chip);
+ struct device *dev;
int base;
u16 ngpio;
unsigned can_sleep:1;
--
1.5.4.1
next prev parent reply other threads:[~2008-04-04 2:08 UTC|newest]
Thread overview: 60+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <200710291809.29936.david-b@pacbell.net>
2007-10-30 1:51 ` [patch/rfc 1/4] GPIO implementation framework David Brownell
2007-11-05 21:05 ` David Brownell
2007-11-13 2:28 ` eric miao
2007-11-13 19:06 ` David Brownell
2007-11-14 0:57 ` eric miao
2007-11-14 1:00 ` eric miao
2007-11-14 1:02 ` eric miao
2007-11-14 1:03 ` eric miao
2007-11-14 1:04 ` eric miao
2007-11-14 1:04 ` eric miao
2007-11-14 4:36 ` David Brownell
2007-11-14 6:51 ` eric miao
2007-11-14 7:19 ` David Brownell
2007-11-14 7:36 ` eric miao
2007-11-17 10:38 ` Jean Delvare
2007-11-17 17:36 ` David Brownell
2007-11-20 15:20 ` Jean Delvare
2007-11-14 4:18 ` David Brownell
2007-11-14 6:46 ` eric miao
2007-11-14 3:28 ` David Brownell
2007-11-14 3:25 ` David Brownell
2007-11-14 3:53 ` David Brownell
2007-11-14 6:37 ` eric miao
2007-11-14 3:30 ` David Brownell
2007-11-14 6:40 ` eric miao
2007-11-14 7:08 ` David Brownell
2007-11-27 1:46 ` David Brownell
2007-11-27 10:58 ` eric miao
2007-11-27 17:26 ` David Brownell
2007-11-27 19:03 ` David Brownell
2007-11-27 19:29 ` David Brownell
2007-11-28 5:11 ` eric miao
2007-11-28 3:15 ` [patch/rfc 2.6.24-rc3-mm] gpiolib grows a gpio_desc David Brownell
2007-11-28 9:10 ` eric miao
2007-11-28 9:53 ` David Brownell
2007-10-30 1:51 ` [patch/rfc 2/4] pcf875x I2C GPIO expander driver David Brownell
2007-11-30 12:32 ` Jean Delvare
2007-11-30 13:04 ` Bill Gatliff
2007-11-30 13:36 ` Jean Delvare
2007-11-30 14:09 ` Bill Gatliff
2007-11-30 18:40 ` David Brownell
2007-11-30 20:13 ` Jean Delvare
2007-11-30 20:59 ` David Brownell
2008-04-04 2:06 ` Trent Piepho [this message]
2008-04-04 2:45 ` Ben Nizette
2008-04-04 3:33 ` Trent Piepho
2008-04-04 4:57 ` Ben Nizette
2008-04-05 4:05 ` userspace GPIO access (WAS: [patch/rfc 2/4] pcf875x ...) David Brownell
2008-04-07 17:56 ` Trent Piepho
2008-04-04 8:09 ` [patch/rfc 2/4] pcf875x I2C GPIO expander driver Jean Delvare
2008-04-04 19:07 ` Trent Piepho
2008-04-04 19:36 ` Jean Delvare
2008-04-04 20:18 ` Trent Piepho
2008-04-05 2:51 ` David Brownell
2008-04-05 2:53 ` David Brownell
2007-12-06 3:03 ` [patch/rfc 2/4] pcf857x " David Brownell
2007-12-06 23:17 ` Jean Delvare
2007-12-07 4:02 ` David Brownell
2007-10-30 1:53 ` [patch/rfc 3/4] DaVinci platform uses new GPIOLIB David Brownell
2007-10-30 1:54 ` [patch/rfc 4/4] DaVinci EVM uses pcf857x GPIO driver David Brownell
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=Pine.LNX.4.64.0804031844500.11928@t2.domain.actdsltmp \
--to=tpiepho@freescale.com \
--cc=david-b@pacbell.net \
--cc=khali@linux-fr.org \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).