Message ID | 1354653588-4018-4-git-send-email-stigge@antcom.de (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 12/04/2012 09:39 PM, Roland Stigge wrote: > This patch adds a character device interface to the block GPIO system. > > Signed-off-by: Roland Stigge <stigge@antcom.de> > --- > Documentation/ABI/testing/dev-gpioblock | 34 +++++ > drivers/gpio/gpiolib.c | 208 +++++++++++++++++++++++++++++++- > include/linux/gpio.h | 10 + > 3 files changed, 251 insertions(+), 1 deletion(-) > > --- /dev/null > +++ linux-2.6/Documentation/ABI/testing/dev-gpioblock > @@ -0,0 +1,34 @@ > +What: /dev/<gpioblock> > +Date: Nov 2012 > +KernelVersion: 3.7 > +Contact: Roland Stigge <stigge@antcom.de> > +Description: The /dev/<gpioblock> character device node provides userspace > + access to GPIO blocks, named exactly as the block, e.g. > + /dev/block0. > + > + Reading: > + When reading sizeof(unsigned long) bytes from the device, the > + current state of the block, masked by the current mask (see > + below) can be obtained as a word. When the device is opened > + with O_NONBLOCK, read() always returns with data immediately, > + otherwise it blocks until data is available, see IRQ handling > + below. > + > + Writing: > + By writing sizeof(unsigned long) bytes to the device, the > + current state of the block can be set. This operation is > + masked by the current mask (see below). > + > + IRQ handling: > + When one or more IRQs in the block are IRQ capable, you can > + poll() on the device to check/wait for this IRQ. If no IRQ > + is available, poll() returns -ENOSYS and userspace needs to > + (busy) poll itself if necessary. > + > + Setting the mask (default: all bits set): > + By doing an ioctl(fd, 0, &mask) with an unsigned long mask, the > + current mask for read and write operations on this gpio block > + can be set. > + > + See also Documentation/gpio.txt for an explanation of block > + GPIO. > --- linux-2.6.orig/drivers/gpio/gpiolib.c > +++ linux-2.6/drivers/gpio/gpiolib.c > @@ -11,6 +11,8 @@ > #include <linux/of_gpio.h> > #include <linux/idr.h> > #include <linux/slab.h> > +#include <linux/uaccess.h> > +#include <linux/poll.h> > > #define CREATE_TRACE_POINTS > #include <trace/events/gpio.h> > @@ -2122,6 +2124,190 @@ struct gpio_block *gpio_block_find_by_na > } > EXPORT_SYMBOL_GPL(gpio_block_find_by_name); > > +static struct gpio_block *gpio_block_find_by_minor(int minor) > +{ > + struct gpio_block *i; > + > + list_for_each_entry(i, &gpio_block_list, list) > + if (i->miscdev.minor == minor) > + return i; > + return NULL; > +} > + > +static bool gpio_block_is_irq_duplicate(struct gpio_block *block, int index) > +{ > + int irq = gpio_to_irq(block->gpio[index]); > + int i; > + > + for (i = 0; i < index; i++) > + if (gpio_to_irq(block->gpio[i]) == irq) > + return true; > + return false; > +} > + > +static irqreturn_t gpio_block_irq_handler(int irq, void *dev) > +{ > + struct gpio_block *block = dev; > + > + wake_up_interruptible(&block->wait_queue); > + block->got_int = true; > + > + return IRQ_HANDLED; > +} > + > +static int gpio_block_fop_open(struct inode *in, struct file *f) > +{ > + int i; > + struct gpio_block *block = gpio_block_find_by_minor(MINOR(in->i_rdev)); > + int status; > + int irq; > + > + if (!block) > + return -ENOENT; > + > + block->irq_controlled = false; > + block->got_int = false; > + init_waitqueue_head(&block->wait_queue); > + f->private_data = block; > + > + for (i = 0; i < block->ngpio; i++) { > + status = gpio_request(block->gpio[i], "gpioblock dev"); You could use the name of the GPIO block. > + if (status) > + goto err1; > + > + irq = gpio_to_irq(block->gpio[i]); > + if (irq >= 0 && > + !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags) && > + !gpio_block_is_irq_duplicate(block, i)) { > + status = request_irq(irq, gpio_block_irq_handler, > + IRQF_TRIGGER_FALLING, > + block->name, block); > + if (status) > + goto err2; > + > + block->irq_controlled = true; > + } > + } There is no need to request IRQs if "O_NONBLOCK" is specified. > + > + return 0; > + > +err1: > + while (i > 0) { > + i--; > + > + irq = gpio_to_irq(block->gpio[i]); > + if (irq >= 0 && > + !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags) && > + !gpio_block_is_irq_duplicate(block, i)) > + free_irq(irq, block); > +err2: > + gpio_free(block->gpio[i]); > + } > + return status; > +} > + > +static int gpio_block_fop_release(struct inode *in, struct file *f) > +{ > + int i; > + struct gpio_block *block = (struct gpio_block *)f->private_data; > + > + for (i = 0; i < block->ngpio; i++) { > + int irq = gpio_to_irq(block->gpio[i]); > + > + if (irq >= 0 && > + !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags) && > + !gpio_block_is_irq_duplicate(block, i)) > + free_irq(irq, block); > + > + gpio_free(block->gpio[i]); > + } > + > + return 0; > +} > + > +static ssize_t gpio_block_fop_read(struct file *f, char __user *buf, size_t n, > + loff_t *offset) > +{ > + struct gpio_block *block = (struct gpio_block *)f->private_data; > + int err; > + > + if (block->irq_controlled) { > + if (!(f->f_flags & O_NONBLOCK)) > + wait_event_interruptible(block->wait_queue, > + block->got_int); > + block->got_int = 0; > + } > + > + if (n >= sizeof(unsigned long)) { > + unsigned long values = gpio_block_get(block, block->cur_mask); > + > + err = put_user(values, (unsigned long __user *)buf); > + if (err) > + return err; > + > + return sizeof(unsigned long); > + } > + return 0; > +} I observed that the read returns once immediately (without blocking) after reboot. I did not look into that yet. Wolfgang.
Hi Wolfgang, On 05/12/12 20:01, Wolfgang Grandegger wrote: >> + for (i = 0; i < block->ngpio; i++) { >> + status = gpio_request(block->gpio[i], "gpioblock dev"); > > You could use the name of the GPIO block. OK. >> + if (status) >> + goto err1; >> + >> + irq = gpio_to_irq(block->gpio[i]); >> + if (irq >= 0 && >> + !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags) && >> + !gpio_block_is_irq_duplicate(block, i)) { >> + status = request_irq(irq, gpio_block_irq_handler, >> + IRQF_TRIGGER_FALLING, >> + block->name, block); >> + if (status) >> + goto err2; >> + >> + block->irq_controlled = true; >> + } >> + } > > There is no need to request IRQs if "O_NONBLOCK" is specified. Sure? Regarding this, I found: "The poll() function shall not be affected by the O_NONBLOCK flag." [1] > I observed that the read returns once immediately (without blocking) > after reboot. I did not look into that yet. Didn't happen to me. Can you tell how this can be reproduced? Thanks, Roland [1] http://pubs.opengroup.org/onlinepubs/009695399/functions/poll.html
On 12/05/2012 11:20 PM, Roland Stigge wrote: > Hi Wolfgang, > > On 05/12/12 20:01, Wolfgang Grandegger wrote: >>> + for (i = 0; i < block->ngpio; i++) { >>> + status = gpio_request(block->gpio[i], "gpioblock dev"); >> >> You could use the name of the GPIO block. > > OK. > >>> + if (status) >>> + goto err1; >>> + >>> + irq = gpio_to_irq(block->gpio[i]); >>> + if (irq >= 0 && >>> + !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags) && >>> + !gpio_block_is_irq_duplicate(block, i)) { >>> + status = request_irq(irq, gpio_block_irq_handler, >>> + IRQF_TRIGGER_FALLING, >>> + block->name, block); >>> + if (status) >>> + goto err2; >>> + >>> + block->irq_controlled = true; >>> + } >>> + } >> >> There is no need to request IRQs if "O_NONBLOCK" is specified. > > Sure? Regarding this, I found: "The poll() function shall not be > affected by the O_NONBLOCK flag." [1] Ah, didn't know that. >> I observed that the read returns once immediately (without blocking) >> after reboot. I did not look into that yet. > > Didn't happen to me. Can you tell how this can be reproduced? I think there is an interrupt pending for some reason. It might not be the case with your setup. Feel free to add my: "Tested by: Wolfgang Grandegger <wg@grandegger.com>". Will do some more tests when time permits, especially with mixed GPIO input and output. Wolfgang.
--- /dev/null +++ linux-2.6/Documentation/ABI/testing/dev-gpioblock @@ -0,0 +1,34 @@ +What: /dev/<gpioblock> +Date: Nov 2012 +KernelVersion: 3.7 +Contact: Roland Stigge <stigge@antcom.de> +Description: The /dev/<gpioblock> character device node provides userspace + access to GPIO blocks, named exactly as the block, e.g. + /dev/block0. + + Reading: + When reading sizeof(unsigned long) bytes from the device, the + current state of the block, masked by the current mask (see + below) can be obtained as a word. When the device is opened + with O_NONBLOCK, read() always returns with data immediately, + otherwise it blocks until data is available, see IRQ handling + below. + + Writing: + By writing sizeof(unsigned long) bytes to the device, the + current state of the block can be set. This operation is + masked by the current mask (see below). + + IRQ handling: + When one or more IRQs in the block are IRQ capable, you can + poll() on the device to check/wait for this IRQ. If no IRQ + is available, poll() returns -ENOSYS and userspace needs to + (busy) poll itself if necessary. + + Setting the mask (default: all bits set): + By doing an ioctl(fd, 0, &mask) with an unsigned long mask, the + current mask for read and write operations on this gpio block + can be set. + + See also Documentation/gpio.txt for an explanation of block + GPIO. --- linux-2.6.orig/drivers/gpio/gpiolib.c +++ linux-2.6/drivers/gpio/gpiolib.c @@ -11,6 +11,8 @@ #include <linux/of_gpio.h> #include <linux/idr.h> #include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/poll.h> #define CREATE_TRACE_POINTS #include <trace/events/gpio.h> @@ -2122,6 +2124,190 @@ struct gpio_block *gpio_block_find_by_na } EXPORT_SYMBOL_GPL(gpio_block_find_by_name); +static struct gpio_block *gpio_block_find_by_minor(int minor) +{ + struct gpio_block *i; + + list_for_each_entry(i, &gpio_block_list, list) + if (i->miscdev.minor == minor) + return i; + return NULL; +} + +static bool gpio_block_is_irq_duplicate(struct gpio_block *block, int index) +{ + int irq = gpio_to_irq(block->gpio[index]); + int i; + + for (i = 0; i < index; i++) + if (gpio_to_irq(block->gpio[i]) == irq) + return true; + return false; +} + +static irqreturn_t gpio_block_irq_handler(int irq, void *dev) +{ + struct gpio_block *block = dev; + + wake_up_interruptible(&block->wait_queue); + block->got_int = true; + + return IRQ_HANDLED; +} + +static int gpio_block_fop_open(struct inode *in, struct file *f) +{ + int i; + struct gpio_block *block = gpio_block_find_by_minor(MINOR(in->i_rdev)); + int status; + int irq; + + if (!block) + return -ENOENT; + + block->irq_controlled = false; + block->got_int = false; + init_waitqueue_head(&block->wait_queue); + f->private_data = block; + + for (i = 0; i < block->ngpio; i++) { + status = gpio_request(block->gpio[i], "gpioblock dev"); + if (status) + goto err1; + + irq = gpio_to_irq(block->gpio[i]); + if (irq >= 0 && + !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags) && + !gpio_block_is_irq_duplicate(block, i)) { + status = request_irq(irq, gpio_block_irq_handler, + IRQF_TRIGGER_FALLING, + block->name, block); + if (status) + goto err2; + + block->irq_controlled = true; + } + } + + return 0; + +err1: + while (i > 0) { + i--; + + irq = gpio_to_irq(block->gpio[i]); + if (irq >= 0 && + !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags) && + !gpio_block_is_irq_duplicate(block, i)) + free_irq(irq, block); +err2: + gpio_free(block->gpio[i]); + } + return status; +} + +static int gpio_block_fop_release(struct inode *in, struct file *f) +{ + int i; + struct gpio_block *block = (struct gpio_block *)f->private_data; + + for (i = 0; i < block->ngpio; i++) { + int irq = gpio_to_irq(block->gpio[i]); + + if (irq >= 0 && + !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags) && + !gpio_block_is_irq_duplicate(block, i)) + free_irq(irq, block); + + gpio_free(block->gpio[i]); + } + + return 0; +} + +static ssize_t gpio_block_fop_read(struct file *f, char __user *buf, size_t n, + loff_t *offset) +{ + struct gpio_block *block = (struct gpio_block *)f->private_data; + int err; + + if (block->irq_controlled) { + if (!(f->f_flags & O_NONBLOCK)) + wait_event_interruptible(block->wait_queue, + block->got_int); + block->got_int = 0; + } + + if (n >= sizeof(unsigned long)) { + unsigned long values = gpio_block_get(block, block->cur_mask); + + err = put_user(values, (unsigned long __user *)buf); + if (err) + return err; + + return sizeof(unsigned long); + } + return 0; +} + +static ssize_t gpio_block_fop_write(struct file *f, const char __user *buf, + size_t n, loff_t *offset) +{ + struct gpio_block *block = (struct gpio_block *)f->private_data; + int err; + + if (n >= sizeof(unsigned long)) { + unsigned long values; + + err = get_user(values, (unsigned long __user *)buf); + if (err) + return err; + if (gpio_block_is_output(block)) + gpio_block_set(block, block->cur_mask, values); + else + return -EPERM; + return sizeof(unsigned long); + } + return 0; +} + +static long gpio_block_fop_ioctl(struct file *f, unsigned int cmd, + unsigned long arg) +{ + struct gpio_block *block = (struct gpio_block *)f->private_data; + unsigned long __user *x = (unsigned long __user *)arg; + + if (cmd == 0) + return get_user(block->cur_mask, x); + + return -EINVAL; +} + +unsigned int gpio_block_fop_poll(struct file *f, struct poll_table_struct *pt) +{ + struct gpio_block *block = (struct gpio_block *)f->private_data; + + if (!block->irq_controlled) + return -ENOSYS; + + if (!block->got_int) + poll_wait(f, &block->wait_queue, pt); + + if (block->got_int) + return POLLIN; + + return 0; +} + +static const struct file_operations gpio_block_fops = { + .open = gpio_block_fop_open, + .release = gpio_block_fop_release, + .read = gpio_block_fop_read, + .write = gpio_block_fop_write, + .unlocked_ioctl = gpio_block_fop_ioctl, + .poll = gpio_block_fop_poll, +}; + int gpio_block_register(struct gpio_block *block) { int ret; @@ -2132,12 +2318,31 @@ int gpio_block_register(struct gpio_bloc list_add(&block->list, &gpio_block_list); ret = gpio_block_export(block); - if (ret) + + /* + * ret == ENOSYS is the case where GPIO_SYSFS is deactivated. In this + * case, we can continue safely anyway, since we can provide the device + * interface. + */ + if (ret && ret != -ENOSYS) goto err1; + block->miscdev.name = block->name; + block->miscdev.nodename = block->name; + block->miscdev.minor = MISC_DYNAMIC_MINOR; + block->miscdev.fops = &gpio_block_fops; + block->miscdev.mode = S_IWUSR | S_IRUGO; + + ret = misc_register(&block->miscdev); + if (ret) + goto err2; + return 0; +err2: + gpio_block_unexport(block); err1: list_del(&block->list); + return ret; } EXPORT_SYMBOL_GPL(gpio_block_register); @@ -2150,6 +2355,7 @@ void gpio_block_unregister(struct gpio_b if (i == block) { list_del(&i->list); gpio_block_unexport(block); + misc_deregister(&block->miscdev); break; } } --- linux-2.6.orig/include/linux/gpio.h +++ linux-2.6/include/linux/gpio.h @@ -4,6 +4,8 @@ #include <linux/errno.h> #include <linux/types.h> #include <linux/list.h> +#include <linux/sched.h> +#include <linux/miscdevice.h> /* see Documentation/gpio.txt */ @@ -89,6 +91,10 @@ struct gpio_block_chip { * @gpio: list of gpios in this block * @list: global list of blocks, maintained by gpiolib * @cur_mask: currently used gpio mask used by userspace API + * @miscdev: userspace API: device + * @wait_queue: userspace API: wait queue waiting for IRQ + * @irq_controlled: userspace API: flag: using IRQ or not + * @got_int: userspace API: change detection via IRQ */ struct gpio_block { struct list_head gbc_list; @@ -99,6 +105,10 @@ struct gpio_block { struct list_head list; unsigned long cur_mask; + struct miscdevice miscdev; + wait_queue_head_t wait_queue; + bool irq_controlled; + bool got_int; }; #ifdef CONFIG_GENERIC_GPIO
This patch adds a character device interface to the block GPIO system. Signed-off-by: Roland Stigge <stigge@antcom.de> --- Documentation/ABI/testing/dev-gpioblock | 34 +++++ drivers/gpio/gpiolib.c | 208 +++++++++++++++++++++++++++++++- include/linux/gpio.h | 10 + 3 files changed, 251 insertions(+), 1 deletion(-)