lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening linux-cve-announce PHC | |
Open Source and information security mailing list archives
| ||
|
Message-Id: <1354830456-17590-3-git-send-email-stigge@antcom.de> Date: Thu, 6 Dec 2012 22:47:32 +0100 From: Roland Stigge <stigge@...com.de> To: gregkh@...uxfoundation.org, grant.likely@...retlab.ca, linus.walleij@...aro.org, linux-kernel@...r.kernel.org, linux-arm-kernel@...ts.infradead.org, w.sang@...gutronix.de, jbe@...gutronix.de, plagnioj@...osoft.com, highguy@...il.com, broonie@...nsource.wolfsonmicro.com, daniel-gl@....net, rmallon@...il.com, tru@...k-microwave.de, sr@...x.de, wg@...ndegger.com Cc: Roland Stigge <stigge@...com.de> Subject: [PATCH 2/6 v10] gpio: Add sysfs support to block GPIO API This patch adds sysfs support to the block GPIO API. Signed-off-by: Roland Stigge <stigge@...com.de> --- Documentation/ABI/testing/sysfs-gpio | 20 ++ drivers/gpio/gpiolib.c | 250 ++++++++++++++++++++++++++++++++++- include/asm-generic/gpio.h | 11 + include/linux/gpio.h | 15 ++ 4 files changed, 295 insertions(+), 1 deletion(-) --- linux-2.6.orig/Documentation/ABI/testing/sysfs-gpio +++ linux-2.6/Documentation/ABI/testing/sysfs-gpio @@ -25,3 +25,23 @@ Description: /label ... (r/o) descriptive, not necessarily unique /ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1) +What: /sys/class/gpioblock/ +Date: October 2012 +KernelVersion: 3.7 +Contact: Roland Stigge <stigge@...com.de> +Description: + + Block GPIO devices are visible in sysfs as soon as they are registered + (e.g. via devicetree definition). For actual I/O use, their "exported" + boolean attribute must be set to "1". Then, the attribute "values" is + created and at the same time, the GPIOs in the block are requested for + exclusive use by sysfs. + + /sys/class/gpioblock + /BLOCKNAME ... for each GPIO block name + /ngpio ... (r/o) number of GPIOs in this group + /exported ... sysfs export state of this group (0, 1) + /value ... current value as 32 or 64 bit integer in hex + (only available if /exported is 1) + /mask ... current mask as 32 or 64 bit integer in hex + (only available if /exported is 1) --- linux-2.6.orig/drivers/gpio/gpiolib.c +++ linux-2.6/drivers/gpio/gpiolib.c @@ -198,7 +198,8 @@ static bool gpio_block_is_output(struct int i; for (i = 0; i < block->ngpio; i++) - if (!test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags)) + if ((block->cur_mask & BIT(i)) && + !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags)) return false; return true; } @@ -984,6 +985,238 @@ static void gpiochip_unexport(struct gpi chip->label, status); } +static ssize_t gpio_block_ngpio_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_block *block = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", block->ngpio); +} + +static ssize_t gpio_block_value_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_block *block = dev_get_drvdata(dev); + + return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" : + "0x%016lx\n", gpio_block_get(block, block->cur_mask)); +} + +static ssize_t gpio_block_value_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + ssize_t status; + struct gpio_block *block = dev_get_drvdata(dev); + unsigned long value; + + status = kstrtoul(buf, 0, &value); + if (status == 0) { + mutex_lock(&sysfs_lock); + if (gpio_block_is_output(block)) { + gpio_block_set(block, block->cur_mask, value); + status = size; + } else { + status = -EPERM; + } + mutex_unlock(&sysfs_lock); + } + return status; +} + +static struct device_attribute +dev_attr_block_value = __ATTR(value, S_IWUSR | S_IRUGO, gpio_block_value_show, + gpio_block_value_store); + +static ssize_t gpio_block_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_block *block = dev_get_drvdata(dev); + + return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" : + "0x%016lx\n", block->cur_mask); +} + +static ssize_t gpio_block_mask_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + ssize_t status; + struct gpio_block *block = dev_get_drvdata(dev); + unsigned long mask; + + status = kstrtoul(buf, 0, &mask); + if (status == 0) + block->cur_mask = mask; + return status; +} + +static struct device_attribute +dev_attr_block_mask = __ATTR(mask, S_IWUSR | S_IRUGO, gpio_block_mask_show, + gpio_block_mask_store); + +static struct class gpio_block_class; + +static int gpio_block_value_is_exported(struct gpio_block *block) +{ + struct device *dev; + struct sysfs_dirent *sd = NULL; + + mutex_lock(&sysfs_lock); + dev = class_find_device(&gpio_block_class, NULL, block, match_export); + if (!dev) + goto out; + + sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value"); + +out: + mutex_unlock(&sysfs_lock); + return !!sd; +} + +static ssize_t gpio_block_exported_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct gpio_block *block = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", gpio_block_value_is_exported(block)); +} + +static int gpio_block_value_export(struct gpio_block *block) +{ + struct device *dev; + int status; + int i; + + mutex_lock(&sysfs_lock); + + for (i = 0; i < block->ngpio; i++) { + status = gpio_request(block->gpio[i], "sysfs"); + if (status) + goto out1; + } + + dev = class_find_device(&gpio_block_class, NULL, block, match_export); + if (!dev) { + status = -ENODEV; + goto out1; + } + + status = device_create_file(dev, &dev_attr_block_value); + if (status) + goto out1; + + status = device_create_file(dev, &dev_attr_block_mask); + if (status) + goto out2; + + mutex_unlock(&sysfs_lock); + return 0; + +out2: + device_remove_file(dev, &dev_attr_block_value); +out1: + while (--i >= 0) + gpio_free(block->gpio[i]); + + mutex_unlock(&sysfs_lock); + return status; +} + +static int gpio_block_value_unexport(struct gpio_block *block) +{ + struct device *dev; + int i; + + dev = class_find_device(&gpio_block_class, NULL, block, match_export); + if (!dev) + return -ENODEV; + + for (i = 0; i < block->ngpio; i++) + gpio_free(block->gpio[i]); + + device_remove_file(dev, &dev_attr_block_value); + device_remove_file(dev, &dev_attr_block_mask); + + return 0; +} + +static ssize_t gpio_block_exported_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + long value; + int status; + struct gpio_block *block = dev_get_drvdata(dev); + int exported = gpio_block_value_is_exported(block); + + status = kstrtoul(buf, 0, &value); + if (status < 0) + goto err; + + if (value != exported) { + if (value) + status = gpio_block_value_export(block); + else + status = gpio_block_value_unexport(block); + if (!status) + status = size; + } else { + status = size; + } +err: + return status; +} + +static struct device_attribute gpio_block_attrs[] = { + __ATTR(exported, S_IWUSR | S_IRUGO, gpio_block_exported_show, + gpio_block_exported_store), + __ATTR(ngpio, S_IRUGO, gpio_block_ngpio_show, NULL), + __ATTR_NULL, +}; + +static struct class gpio_block_class = { + .name = "gpioblock", + .owner = THIS_MODULE, + + .dev_attrs = gpio_block_attrs, +}; + +int gpio_block_export(struct gpio_block *block) +{ + int status = 0; + struct device *dev; + + /* can't export until sysfs is available ... */ + if (!gpio_block_class.p) { + pr_debug("%s: called too early!\n", __func__); + return -ENOENT; + } + + mutex_lock(&sysfs_lock); + dev = device_create(&gpio_block_class, NULL, MKDEV(0, 0), block, + block->name); + if (IS_ERR(dev)) + status = PTR_ERR(dev); + mutex_unlock(&sysfs_lock); + + return status; +} +EXPORT_SYMBOL_GPL(gpio_block_export); + +void gpio_block_unexport(struct gpio_block *block) +{ + struct device *dev; + + mutex_lock(&sysfs_lock); + dev = class_find_device(&gpio_block_class, NULL, block, match_export); + if (dev) + device_unregister(dev); + mutex_unlock(&sysfs_lock); +} +EXPORT_SYMBOL_GPL(gpio_block_unexport); + static int __init gpiolib_sysfs_init(void) { int status; @@ -994,6 +1227,10 @@ static int __init gpiolib_sysfs_init(voi if (status < 0) return status; + status = class_register(&gpio_block_class); + if (status < 0) + return status; + /* Scan and register the gpio_chips which registered very * early (e.g. before the class_register above was called). * @@ -1724,6 +1961,7 @@ struct gpio_block *gpio_block_create(uns block->name = name; block->ngpio = size; + block->cur_mask = ~0; INIT_LIST_HEAD(&block->gbc_list); block->gpio = kzalloc(sizeof(*block->gpio) * size, GFP_KERNEL); if (!block->gpio) @@ -1886,12 +2124,21 @@ EXPORT_SYMBOL_GPL(gpio_block_find_by_nam int gpio_block_register(struct gpio_block *block) { + int ret; + if (gpio_block_find_by_name(block->name)) return -EBUSY; list_add(&block->list, &gpio_block_list); + ret = gpio_block_export(block); + if (ret) + goto err1; + return 0; +err1: + list_del(&block->list); + return ret; } EXPORT_SYMBOL_GPL(gpio_block_register); @@ -1902,6 +2149,7 @@ void gpio_block_unregister(struct gpio_b list_for_each_entry(i, &gpio_block_list, list) if (i == block) { list_del(&i->list); + gpio_block_unexport(block); break; } } --- linux-2.6.orig/include/asm-generic/gpio.h +++ linux-2.6/include/asm-generic/gpio.h @@ -213,6 +213,8 @@ extern int gpio_export_link(struct devic unsigned gpio); extern int gpio_sysfs_set_active_low(unsigned gpio, int value); extern void gpio_unexport(unsigned gpio); +extern int gpio_block_export(struct gpio_block *block); +extern void gpio_block_unexport(struct gpio_block *block); #endif /* CONFIG_GPIO_SYSFS */ @@ -272,6 +274,15 @@ static inline int gpio_sysfs_set_active_ static inline void gpio_unexport(unsigned gpio) { } + +static inline int gpio_block_export(struct gpio_block *block) +{ + return -ENOSYS; +} + +static inline void gpio_block_unexport(struct gpio_block *block) +{ +} #endif /* CONFIG_GPIO_SYSFS */ #endif /* _ASM_GENERIC_GPIO_H */ --- linux-2.6.orig/include/linux/gpio.h +++ linux-2.6/include/linux/gpio.h @@ -88,6 +88,7 @@ struct gpio_block_chip { * @ngpio: number of gpios in this block * @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 */ struct gpio_block { struct list_head gbc_list; @@ -97,6 +98,7 @@ struct gpio_block { unsigned *gpio; struct list_head list; + unsigned long cur_mask; }; #ifdef CONFIG_GENERIC_GPIO @@ -314,6 +316,19 @@ static inline void gpio_unexport(unsigne WARN_ON(1); } +static inline int gpio_block_export(struct gpio_block *block) +{ + /* GPIO block can never have been requested or set as {in,out}put */ + WARN_ON(1); + return -EINVAL; +} + +static inline void gpio_block_unexport(struct gpio_block *block) +{ + /* GPIO block can never have been exported */ + WARN_ON(1); +} + static inline int gpio_to_irq(unsigned gpio) { /* GPIO can never have been requested or set as input */ -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@...r.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists