Re: [PATCH v2 RESEND] gpiolib: Add pin change notification
On Mon, 2008-10-27 at 12:46 -0700, David Brownell wrote: By the way ... I noticed some new use cases for this sort of mechanism. In the OMAP git tree, say the copy at http://git.kernel.org/?p=linux/kernel/git/tmlind/linux-omap-2.6.git Have a look at arch/arm/plat-omap/gpio-switch.c ... current users include mach-omap2/board-n800.c and mach-omap1/board-palmte.c (in that tree), and report things like headphone jack insertion. Some of that fanciness seems like it shouldn't live in-kernel. I'll be glad to see the need for that driver vanish (except for the need to upgrade userspace, sigh), because of a more generic mechanism existing. :) Cool, I'll check it out. On Monday 20 October 2008, Ben Nizette wrote: This adds pin change notification to the gpiolib sysfs interface. It requires 16 extra bytes in gpio_desc iff CONFIG_GPIO_SYSFS which in turn means, eg, 4k of .bss usage on AVR32. Which seems quite problematic to me, for a mechanism that will in most cases not be used and certainly doesn't have the same level of fast-path considerations as gpio get/set operations. How about using a linux/idr.h table to map GPIOs to some notification structure ... and only when such notifications have been configured? A busy system might have one IDR and a handful of 16 byte structs ... lots less than 4KB, and coming out of kmalloc heaps (and thus barely observable). Yeah since akpm's review a few days back, a few things have been rearranged resulting in this figure blowing out to 13k on AVR32. Obviously not acceptable so I'm looking for a nicer way around this. I've been looking at such an IDR, I think it's the way to go yeah. Due to limitations in sysfs, this patch makes poll(2) and friends work as expected on the value attribute, though reads on value will never block and there is no facility for async reads and writes. I like poll() and friends working, and the rest seems like it's not something to worry about here. Have you thought about how to leverage the interrupt signals on chips like the pcf857x and pca953x, which have dedicated pin changed signals? They're nothing like the real interrupts, with per-pin irq status and disable masks, found in most SOC-integrated GPIO banks (and in fancier discrete ones). So they seem to me like bad matches for genirq... though I might be persuaded otherwise. Effectively, those IRQ are poll now advice ... advice that is not yet used. (The IRQs all seem to stay active until the GPIO bank status is read, FYI, so you can easily imagine how to fake out some IRQ-ish mechanism.) So long as I allow the interrupt to be shared that should work fine (assuming gpio_to_irq returns the irq of the chip-wide pin change signal for all pins on the chip). akpm pointed out that sysfs_notify() cannot be called from interrupt context so now all irqs are really taken as poll now advice. --- a/Documentation/gpio.txt +++ b/Documentation/gpio.txt @@ -529,6 +529,21 @@ and have the following read/write attrib is configured as an output, this value may be written; any nonzero value is treated as high. + notify ... Selects a method for the detection of pin change + events. This can be written or read as one of none, + irq or a number. If none, no detection will be + done, if irq then the change detection will be done + by registering an interrupt handler on the line given + by gpio_to_irq for that gpio. If a number is written, + the gpio will be polled for a state change every n + milliseconds where n is the number written. The + minimum interval is 10 milliseconds. That 10 milliseconds seems a bit arbitrary. By analogy to the RTC framework's handling of periodic IRQs, maybe this should be a limit that a privileged user can change. Right. Should this be a sysfs attribute of the gpio class? That seems a sane place to stick it IMO. IMO it's worth noting that polling modes should not be used if IRQs could work ... and that polling *will* miss all changes that happen between polls. Right, changed. + notify_filter ... This can be written and read as one of + rising, falling or both. If rising, only + rising edges will be reported, similarly for falling + and both. See below ... maybe writing those values to notify should be allowed, for IRQ-driven notification. Then this wouldn't be needed except when polling. And have this attribute appear and disappear? This would break the symmetry between irq and poll driven notification which I quite like. IMO how the change is sensed and whether the change is reported are fairly separate. Though I do agree with your point that we should probably cater for chips which don't support both edge notification, see below. + GPIO controllers have paths like
Re: [PATCH v2 RESEND] gpiolib: Add pin change notification
On Tue, 21 Oct 2008 09:50:06 +1100 Ben Nizette [EMAIL PROTECTED] wrote: This adds pin change notification to the gpiolib sysfs interface. It requires 16 extra bytes in gpio_desc iff CONFIG_GPIO_SYSFS which in turn means, eg, 4k of .bss usage on AVR32. Due to limitations in sysfs, this patch makes poll(2) and friends work as expected on the value attribute, though reads on value will never block and there is no facility for async reads and writes. ... +struct poll_desc *work_to_poll(struct work_struct *ws) static +{ + return container_of((struct delayed_work *)ws, struct poll_desc, work); +} + +void gpio_poll_work(struct work_struct *ws) static +{ + struct poll_desc*poll = work_to_poll(ws); + + unsignedgpio = poll-gpio; + struct gpio_desc*desc = gpio_desc[gpio]; + + int new = gpio_get_value_cansleep(gpio); + int old = desc-val; + + if ((new !old test_bit(ASYNC_RISING, desc-flags)) || + (!new old test_bit(ASYNC_FALLING, desc-flags))) + sysfs_notify(desc-dev-kobj, NULL, value); + + desc-val = new; + schedule_delayed_work(poll-work, desc-timeout); +} + +static irqreturn_t gpio_irq_handler(int irq, void *dev_id) +{ + struct gpio_desc *desc = dev_id; + int gpio = desc - gpio_desc; + int new, old; + + if (!gpio_is_valid(gpio)) + return IRQ_NONE; + + new = gpio_get_value(gpio); + old = desc-val; + + if ((new !old test_bit(ASYNC_RISING, desc-flags)) || + (!new old test_bit(ASYNC_FALLING, desc-flags))) + sysfs_notify(desc-dev-kobj, NULL, value); eekeekeek! sysfs_notify() does mutex_lock() and will die horridly if called from an interrupt handler. You should have got a storm of warnings when runtime testing this code. Please ensure that all debug options are enabled when testing code. Documentation/SubmitChecklist has help. + desc-val = new; + + return IRQ_HANDLED; +} + static ssize_t gpio_direction_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -284,9 +351,162 @@ static ssize_t gpio_value_store(struct d static /*const*/ DEVICE_ATTR(value, 0644, gpio_value_show, gpio_value_store); +static ssize_t gpio_notify_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + ssize_t ret; + + mutex_lock(sysfs_lock); + + if (test_bit(ASYNC_MODE_IRQ, desc-flags)) + ret = sprintf(buf, irq\n); + else if (test_bit(ASYNC_MODE_POLL, desc-flags)) + ret = sprintf(buf, %ld\n, desc-timeout * 1000 / HZ); + else + ret = sprintf(buf, none\n); + + mutex_unlock(sysfs_lock); + return ret; +} panics oh, sysfs_lock is a gpiolib-local thing, not a sysfs-core thing. Crappy name. -- To unsubscribe from this list: send the line unsubscribe linux-embedded in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2 RESEND] gpiolib: Add pin change notification
This adds pin change notification to the gpiolib sysfs interface. It requires 16 extra bytes in gpio_desc iff CONFIG_GPIO_SYSFS which in turn means, eg, 4k of .bss usage on AVR32. Due to limitations in sysfs, this patch makes poll(2) and friends work as expected on the value attribute, though reads on value will never block and there is no facility for async reads and writes. Signed-off-by: Ben Nizette [EMAIL PROTECTED] --- Right, there seems to be a new outgoing mail server between me and you which viciously attacks tabulations. Until I find the monkey responsible please accept the patch as an attachment along with my apologies. Documentation/gpio.txt | 48 + drivers/gpio/gpiolib.c | 246 - 2 files changed, 289 insertions(+), 5 deletions(-) diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt index b1b9887..998a40f 100644 --- a/Documentation/gpio.txt +++ b/Documentation/gpio.txt @@ -529,6 +529,21 @@ and have the following read/write attributes: is configured as an output, this value may be written; any nonzero value is treated as high. + notify ... Selects a method for the detection of pin change + events. This can be written or read as one of none, + irq or a number. If none, no detection will be + done, if irq then the change detection will be done + by registering an interrupt handler on the line given + by gpio_to_irq for that gpio. If a number is written, + the gpio will be polled for a state change every n + milliseconds where n is the number written. The + minimum interval is 10 milliseconds. + + notify_filter ... This can be written and read as one of + rising, falling or both. If rising, only + rising edges will be reported, similarly for falling + and both. + GPIO controllers have paths like /sys/class/gpio/chipchip42/ (for the controller implementing GPIOs starting at #42) and have the following read-only attributes: @@ -549,6 +564,39 @@ gpiochip nodes (possibly in conjunction with schematics) to determine the correct GPIO number to use for a given signal. +Pin Change Notification +--- +The value attribute of a gpio is pollable. For this to work, you +must have set up the notify attribute of that gpio to the type of +detection suitable for the underlying hardware. + +If the hardware supports interrupts on both edges and the gpio to +interrupt line mapping is unique, you should write irq to the notify +attribute. + +If the hardware doesn't support this, or you're unsure, you can write +a number to the notify attribute. This number is the delay between +successive polls of the gpio state in milliseconds. You may not poll +more often than 10ms intervals. + +You must use poll mode if communication with the gpio's chip requires +sleeping. This is the case if, for example, the gpio is part of a +I2C or SPI GPIO expander. + +By default, both rising and falling edges are reported. To filter +this, you can write one of rising or falling to the notify_filter +attribute. To go back to being notified of both edge changes, write +both to this attribute. + +Once the notification has been set up, you may use poll(2) and friends +on the value attribute. This attribute will register as having new +data ready to be read if and only if there has been a state change +since the last read(2) to the value attribute. + +Note that unlike a regular device file, a read on the value attribute +will never block whether or not there's new data to be read. + + Exporting from Kernel code -- Kernel code can explicitly manage exports of GPIOs which have already been diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 9112830..f43f231 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1,6 +1,7 @@ #include linux/kernel.h #include linux/module.h #include linux/irq.h +#include linux/interrupt.h #include linux/spinlock.h #include linux/device.h #include linux/err.h @@ -49,13 +50,35 @@ struct gpio_desc { #define FLAG_RESERVED 2 #define FLAG_EXPORT 3 /* protected by sysfs_lock */ #define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */ +#define ASYNC_MODE_IRQ 5 /* using interrupts for async notification */ +#define ASYNC_MODE_POLL 6 /* using polling for async notification */ +#define ASYNC_RISING 7 /* will notify on rising edges */ +#define ASYNC_FALLING 8 /* will notify on falling edges */ #ifdef CONFIG_DEBUG_FS const char *label; #endif + +#ifdef CONFIG_GPIO_SYSFS + struct device *dev; + struct poll_desc *poll; + + /* Poll interval in jiffies, here (rather than in struct poll_desc + * so the user can change the value no matter what their current + * notification mode is */ + long timeout; + + /* Last known value */ + int val; +#endif }; static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; +struct poll_desc { + struct delayed_work work; + unsigned gpio; +}; + static inline void desc_set_label(struct gpio_desc