gpio_export_with_name

对于Linux 内核中的gpio操作,想必大家都很清楚,一般的做法是通过 gpio 的 sysfs文件系统

cd /sys/class/gpio
echo 11 > export
cd gpio11
xxxxxxxxx......

这种方法,可以直接操作GPIO口,但是当我们需要将类似刚刚例子里面的gpio11替换为我们使用引脚的具体定义时,如PWR_EN,这样的方法就行不通了,
所以这里我们要重写 Linux 的gpio_exportgpio_export_with_name

diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index d9fa7985..3d539600 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -604,6 +604,111 @@ fail_unlock:
 }
 EXPORT_SYMBOL_GPL(gpiod_export);
 
+/**
+ * gpiod_export_with_name - export a GPIO through sysfs
+ * @gpio: gpio to make available, already requested
+ * @direction_may_change: true if userspace may change gpio direction
+ * @name: gpio name
+ * Context: arch_initcall or later
+ *
+ * When drivers want to make a GPIO accessible to userspace after they
+ * have requested it -- perhaps while debugging, or as part of their
+ * public interface -- they may use this routine.  If the GPIO can
+ * change direction (some can't) and the caller allows it, userspace
+ * will see "direction" sysfs attribute which may be used to change
+ * the gpio's direction.  A "value" attribute will always be provided.
+ *
+ * Returns zero on success, else an error.
+ */
+int gpiod_export_with_name(struct gpio_desc *desc, bool direction_may_change,
+                            const char *name)
+{
+	struct gpio_chip	*chip;
+	unsigned long		flags;
+	int			status;
+	const char		*ioname = NULL;
+	struct device		*dev;
+	int			offset;
+
+	/* can't export until sysfs is available ... */
+	if (!gpio_class.p) {
+		pr_debug("%s: called too early!\n", __func__);
+		return -ENOENT;
+	}
+
+	if (!desc) {
+		pr_debug("%s: invalid gpio descriptor\n", __func__);
+		return -EINVAL;
+	}
+
+	chip = desc->chip;
+
+	mutex_lock(&sysfs_lock);
+
+	/* check if chip is being removed */
+	if (!chip || !chip->exported) {
+		status = -ENODEV;
+		goto fail_unlock;
+	}
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
+	     test_bit(FLAG_EXPORT, &desc->flags)) {
+		spin_unlock_irqrestore(&gpio_lock, flags);
+		gpiod_dbg(desc, "%s: unavailable (requested=%d, exported=%d)\n",
+				__func__,
+				test_bit(FLAG_REQUESTED, &desc->flags),
+				test_bit(FLAG_EXPORT, &desc->flags));
+		status = -EPERM;
+		goto fail_unlock;
+	}
+
+	if (!desc->chip->direction_input || !desc->chip->direction_output)
+		direction_may_change = false;
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	offset = gpio_chip_hwgpio(desc);
+	//if (desc->chip->names && desc->chip->names[offset])
+	//	ioname = desc->chip->names[offset];
+	ioname = name;			//这里最关键
+
+	dev = device_create_with_groups(&gpio_class, desc->chip->dev,
+					MKDEV(0, 0), desc, gpio_groups,
+					ioname ? ioname : "gpio%u",
+					desc_to_gpio(desc));
+	if (IS_ERR(dev)) {
+		status = PTR_ERR(dev);
+		goto fail_unlock;
+	}
+
+	if (direction_may_change) {
+		status = device_create_file(dev, &dev_attr_direction);
+		if (status)
+			goto fail_unregister_device;
+	}
+
+	if (gpiod_to_irq(desc) >= 0 && (direction_may_change ||
+				       !test_bit(FLAG_IS_OUT, &desc->flags))) {
+		status = device_create_file(dev, &dev_attr_edge);
+		if (status)
+			goto fail_remove_attr_direction;
+	}
+
+	set_bit(FLAG_EXPORT, &desc->flags);
+	mutex_unlock(&sysfs_lock);
+	return 0;
+
+fail_remove_attr_direction:
+	device_remove_file(dev, &dev_attr_direction);
+fail_unregister_device:
+	device_unregister(dev);
+fail_unlock:
+	mutex_unlock(&sysfs_lock);
+	gpiod_dbg(desc, "%s: status %d\n", __func__, status);
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpiod_export_with_name);
+
 static int match_export(struct device *dev, const void *data)
 {
 	return dev_get_drvdata(dev) == data;

头文件相关

diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index d38a2948..ccdbe493 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -113,7 +113,8 @@ static inline int __gpio_to_irq(unsigned gpio)
 extern int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
 extern int gpio_request_array(const struct gpio *array, size_t num);
 extern void gpio_free_array(const struct gpio *array, size_t num);
-
+extern int gpiod_export_with_name(struct gpio_desc *desc, bool direction_may_change,
+                                    const char *name);
 /*
  * A sysfs interface can be exported by individual drivers if they want,
  * but more typically is configured entirely from userspace.
@@ -123,6 +124,12 @@ static inline int gpio_export(unsigned gpio, bool direction_may_change)
 	return gpiod_export(gpio_to_desc(gpio), direction_may_change);
 }
 
+static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change,
+                                        const char *name)
+{
+	return gpiod_export_with_name(gpio_to_desc(gpio), direction_may_change, name);
+}
+
 static inline int gpio_export_link(struct device *dev, const char *name,
 				   unsigned gpio)
 {
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index 85aa5d0b..a43c7e6b 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -188,6 +188,14 @@ static inline int gpio_export(unsigned gpio, bool direction_may_change)
 	return -EINVAL;
 }
 
+static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change,
+                                        const char *name)
+{
+	/* GPIO can never have been requested or set as {in,out}put */
+	WARN_ON(1);
+	return -EINVAL;
+}
+
 static inline int gpio_export_link(struct device *dev, const char *name,
 				unsigned gpio)
 {

设备树的方式

dts 修改

+
+    gpio_export {
+        compatible = "gpio-export";
+        #size-cells = <0>;
+
+        kc_ctl1 {
+            gpio-export,name = "KC_CTL1";
+            gpio-export,output = <0>;
+            gpio-export,direction_may_change;
+            gpios = <&gpio3 RK_PB1 GPIO_ACTIVE_HIGH>;
+        };
+
+        kc_ctl2 {
+            gpio-export,name = "KC_CTL2";
+            gpio-export,output = <0>;
+            gpio-export,direction_may_change;
+            gpios = <&gpio3 RK_PB2 GPIO_ACTIVE_HIGH>;
+        };
+    };

增加驱动解析

diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index e0f149bdf98f..a85dd7a9f37f 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -23,6 +23,9 @@
 #include <linux/pinctrl/pinctrl.h>
 #include <linux/slab.h>
 #include <linux/gpio/machine.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/init.h>

 #include "gpiolib.h"

@@ -660,3 +663,66 @@ void of_gpiochip_remove(struct gpio_chip *chip)
     gpiochip_remove_pin_ranges(chip);
     of_node_put(chip->of_node);
 }
+
+static struct of_device_id gpio_export_ids[] = {
+    { .compatible = "gpio-export" },
+    { /* sentinel */ }
+};
+
+static int gpio_export_name_probe(struct platform_device *pdev)
+{
+    struct device_node *np = pdev->dev.of_node;
+    struct device_node *cnp;
+    u32 val;
+    int nb = 0;
+
+    for_each_child_of_node(np, cnp) {
+        const char *name = NULL;
+        int gpio;
+        bool dmc;
+        int max_gpio = 1;
+        int i;
+
+        of_property_read_string(cnp, "gpio-export,name", &name);
+
+        if (!name)
+            max_gpio = of_gpio_count(cnp);
+
+        for (i = 0; i < max_gpio; i++) {
+            unsigned flags = 0;
+            enum of_gpio_flags of_flags;
+
+            gpio = of_get_gpio_flags(cnp, i, &of_flags);
+
+            if (of_flags == OF_GPIO_ACTIVE_LOW)
+                flags |= GPIOF_ACTIVE_LOW;
+
+            if (!of_property_read_u32(cnp, "gpio-export,output", &val))
+                flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+            else
+                flags |= GPIOF_IN;
+
+            if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np)))
+                continue;
+
+            dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change");
+            gpio_export_with_name(gpio, dmc, name);
+            nb++;
+        }
+    }
+
+    dev_info(&pdev->dev, "%d gpio(s) exported\n", nb);
+
+    return 0;
+}
+
+static struct platform_driver gpio_export_name_driver = {
+    .probe = gpio_export_name_probe,
+    .driver     = {
+        .name       = "gpio-export",
+        .owner  = THIS_MODULE,
+        .of_match_table = of_match_ptr(gpio_export_ids),
+    },
+};
+
+module_platform_driver(gpio_export_name_driver);
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index e0ccc79b239a..28942cfe80cf 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -661,6 +661,109 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
 }
 EXPORT_SYMBOL_GPL(gpiod_export);

+/**
+ * gpiod_export_with_name - export a GPIO through sysfs
+ * @desc: GPIO to make available, already requested
+ * @direction_may_change: true if userspace may change GPIO direction
+ * @name: gpio name
+ * Context: arch_initcall or later
+ *
+ * When drivers want to make a GPIO accessible to userspace after they
+ * have requested it -- perhaps while debugging, or as part of their
+ * public interface -- they may use this routine.  If the GPIO can
+ * change direction (some can't) and the caller allows it, userspace
+ * will see "direction" sysfs attribute which may be used to change
+ * the gpio's direction.  A "value" attribute will always be provided.
+ *
+ * Returns zero on success, else an error.
+ */
+int gpiod_export_with_name(struct gpio_desc *desc, bool direction_may_change,
+        const char *name)
+{
+    struct gpio_chip    *chip;
+    struct gpio_device  *gdev;
+    struct gpiod_data   *data;
+    unsigned long       flags;
+    int         status;
+    const char      *ioname = NULL;
+    struct device       *dev;
+    int         offset;
+
+    /* can't export until sysfs is available ... */
+    if (!gpio_class.p) {
+        pr_debug("%s: called too early!\n", __func__);
+        return -ENOENT;
+    }
+
+    if (!desc) {
+        pr_debug("%s: invalid gpio descriptor\n", __func__);
+        return -EINVAL;
+    }
+
+    gdev = desc->gdev;
+    chip = gdev->chip;
+
+    mutex_lock(&sysfs_lock);
+
+    /* check if chip is being removed */
+    if (!chip || !gdev->mockdev) {
+        status = -ENODEV;
+        goto err_unlock;
+    }
+
+    spin_lock_irqsave(&gpio_lock, flags);
+    if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
+         test_bit(FLAG_EXPORT, &desc->flags)) {
+        spin_unlock_irqrestore(&gpio_lock, flags);
+        gpiod_dbg(desc, "%s: unavailable (requested=%d, exported=%d)\n",
+                __func__,
+                test_bit(FLAG_REQUESTED, &desc->flags),
+                test_bit(FLAG_EXPORT, &desc->flags));
+        status = -EPERM;
+        goto err_unlock;
+    }
+    spin_unlock_irqrestore(&gpio_lock, flags);
+
+    data = kzalloc(sizeof(*data), GFP_KERNEL);
+    if (!data) {
+        status = -ENOMEM;
+        goto err_unlock;
+    }
+
+    data->desc = desc;
+    mutex_init(&data->mutex);
+    if (chip->direction_input && chip->direction_output)
+        data->direction_can_change = direction_may_change;
+    else
+        data->direction_can_change = false;
+
+    offset = gpio_chip_hwgpio(desc);
+    //if (chip->names && chip->names[offset])
+    //  ioname = chip->names[offset];
+    ioname = name;
+
+    dev = device_create_with_groups(&gpio_class, &gdev->dev,
+                    MKDEV(0, 0), data, gpio_groups,
+                    ioname ? ioname : "gpio%u",
+                    desc_to_gpio(desc));
+    if (IS_ERR(dev)) {
+        status = PTR_ERR(dev);
+        goto err_free_data;
+    }
+
+    set_bit(FLAG_EXPORT, &desc->flags);
+    mutex_unlock(&sysfs_lock);
+    return 0;
+
+err_free_data:
+    kfree(data);
+err_unlock:
+    mutex_unlock(&sysfs_lock);
+    gpiod_dbg(desc, "%s: status %d\n", __func__, status);
+    return status;
+}
+EXPORT_SYMBOL_GPL(gpiod_export_with_name);
+
 static int match_export(struct device *dev, const void *desc)
 {
     struct gpiod_data *data = dev_get_drvdata(dev);
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 19eadac415c4..84c18bb3dbc6 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -117,7 +117,8 @@ static inline int __gpio_to_irq(unsigned gpio)
 extern int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
 extern int gpio_request_array(const struct gpio *array, size_t num);
 extern void gpio_free_array(const struct gpio *array, size_t num);
-
+extern int gpiod_export_with_name(struct gpio_desc *desc, bool direction_may_change,
+                                    const char *name);
 /*
  * A sysfs interface can be exported by individual drivers if they want,
  * but more typically is configured entirely from userspace.
@@ -127,6 +128,13 @@ static inline int gpio_export(unsigned gpio, bool direction_may_change)
     return gpiod_export(gpio_to_desc(gpio), direction_may_change);
 }

+static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change,
+                                        const char *name)
+{
+    return gpiod_export_with_name(gpio_to_desc(gpio), direction_may_change, name);
+}
+
+
 static inline int gpio_export_link(struct device *dev, const char *name,
                    unsigned gpio)
 {
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index b3115d1a7d49..96479fe68316 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -199,6 +199,14 @@ static inline int gpio_export(unsigned gpio, bool direction_may_change)
     return -EINVAL;
 }

+static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change,
+        const char *name)
+{
+    /* GPIO can never have been requested or set as {in,out}put */
+    WARN_ON(1);
+    return -EINVAL;
+}
+
 static inline int gpio_export_link(struct device *dev, const char *name,
                 unsigned gpio)
 {
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值