15 内核里leds-gpio设备驱动的设备树方法

本文介绍如何在Linux内核中配置和使用leds-gpio设备驱动,通过platform_device或设备树来匹配硬件资源,并展示了具体的代码实现及设备树配置示例。

在linux内核里已提供了连接到gpio的led设备驱动,只需要通过platform_device或设备提供相应的硬件资源即可.
使用platform_device方法可参考: http://blog.youkuaiyun.com/jklinux/article/details/73850470

内核里的leds-gpio设备驱动配置选项:

make menuconfig ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-
  Device Drivers  --->
    -*- LED Support  --->
      <*>   LED Support for GPIO connected LEDs

通过配置选项的帮助信息可以查出对应的设备驱动源文件: drivers/leds/leds-gpio.c 


leds-gpio.c源文件里设备树相关的主要内容:

274 static struct platform_driver gpio_led_driver = {
275     .probe      = gpio_led_probe,
276     .shutdown   = gpio_led_shutdown,
277     .driver     = {
278         .name   = "leds-gpio",
279         .of_match_table = of_gpio_leds_match,
280     },
281 };

223 static const struct of_device_id of_gpio_leds_match[] = {
224     { .compatible = "gpio-leds", },
225     {},
226 };
227 
228 MODULE_DEVICE_TABLE(of, of_gpio_leds_match);

这两部分内容可得知,此设备驱动是一个platform_driver的对象,可通过platform_device或设备树里的设备节点来匹配。设备节点里的compatible属性值应是"gpio-leds".

匹配上后, 设备驱动里gpio_led_probe函数就会被触发调用. 在probe函数里就会取出设备树里提供的硬件资源.

230 static int gpio_led_probe(struct platform_device *pdev)
231 {
232     struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev); //使用设备树的方式, pdata应为NULL;
233     struct gpio_leds_priv *priv;
234     int i, ret = 0;
235 
236     if (pdata && pdata->num_leds) {
        ... //使用platform_device方式的获取硬件资源的处理代码
251     } else {
252         priv = gpio_leds_create(pdev); //使用设备树时的处理代码
253         if (IS_ERR(priv))
254             return PTR_ERR(priv);
255     }

在gpio_leds_create函数里获取设备节点的资源
156 static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
157 {
158     struct device *dev = &pdev->dev;
159     struct fwnode_handle *child;
160     struct gpio_leds_priv *priv;
161     int count, ret;
162 
163     count = device_get_child_node_count(dev); //获取子节点的个数, 意味着设备节点里是需要包含子节点的,并不是使用设备节点的属性来的提供资源.
164     if (!count)
165         return ERR_PTR(-ENODEV);
166 
167     priv = devm_kzalloc(dev, sizeof_gpio_leds_priv(count), GFP_KERNEL);
168     if (!priv)
169         return ERR_PTR(-ENOMEM);
171     device_for_each_child_node(dev, child) { //遍历设备节点里的每一个子节点
172         struct gpio_led_data *led_dat = &priv->leds[priv->num_leds];
173         struct gpio_led led = {};
174         const char *state = NULL;
175         struct device_node *np = to_of_node(child);
176 
177         ret = fwnode_property_read_string(child, "label", &led.name); //获取子节点的label属性值. 意味着每个子节点应有一个label属性,属性值应为字符串.
178         if (ret && IS_ENABLED(CONFIG_OF) && np)
179             led.name = np->name;
180         if (!led.name) {
181             fwnode_handle_put(child);
182             return ERR_PTR(-EINVAL);
183         }
184         //获取子节点里的gpio口信息. con_id为NULL,意味着子节点应是使用gpios属性来提供led所连接的io口信息. 而且这里仅是获取一个io口信息,并不是多个io口,意味着每个子节点表示一个led灯的资源
185         led.gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL, child,
186                                  GPIOD_ASIS,
187                                  led.name);
188         if (IS_ERR(led.gpiod)) { //获取io口失败则返回错误码
189             fwnode_handle_put(child);
190             return ERR_CAST(led.gpiod);
191         }
192         //下面的属性应是可选设置的,因并没有不设值而返回错误码.
193         fwnode_property_read_string(child, "linux,default-trigger",
194                         &led.default_trigger);
196         if (!fwnode_property_read_string(child, "default-state",
197                          &state)) {
198             if (!strcmp(state, "keep"))
199                 led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
200             else if (!strcmp(state, "on"))
201                 led.default_state = LEDS_GPIO_DEFSTATE_ON;
202             else
203                 led.default_state = LEDS_GPIO_DEFSTATE_OFF;
204         }
205 
206         if (fwnode_property_present(child, "retain-state-suspended"))
207             led.retain_state_suspended = 1;
208         if (fwnode_property_present(child, "panic-indicator"))
209             led.panic_indicator = 1;
210 
211         ret = create_gpio_led(&led, led_dat, dev, NULL);
212         if (ret < 0) {
213             fwnode_handle_put(child);
214             return ERR_PTR(ret);
215         }
216         led_dat->cdev.dev->of_node = np;
217         priv->num_leds++;
218     }
219 
220     return priv;
221 }

现板上的STATUS-LED连接到PA17, PWR-LED连接到PL10.
综上所述,关于leds-gpio设备驱动的设备树可:

myleds {
    compatible = "gpio-leds";

    superled0 {
        label = "led0";
        gpios = <&pio  0  17  GPIO_ACTIVE_HIGH>;
    };

    superled1 {
        label = "led1";
        gpios = <&r_pio  0  10  GPIO_ACTIVE_HIGH>;
    };
};  


重编设备树并更新后启动系统, 然后就可以查看到myleds设备节点产生platform_device信息:
^_^ / # ls /sys/bus/platform/devices/myleds/
driver/          leds/            of_node/         subsystem/
driver_override  modalias         power/           uevent

//看到有driver目录即表示已与platform_driver匹配上了.

^_^ / # ls /sys/bus/platform/devices/myleds/leds/led
led0/  led1/    
//表示在led子系统里会创建出/sys/class/leds/led0   /sys/class/leds/led1目录

控制led:
echo 1 > /sys/class/leds/led0/brightness   // label为led0的led灯亮
echo 0 > /sys/class/leds/led0/brightness   // label为led0的led灯灭

如无法通过代码分析出设备树里应怎样描述设备,可以参考内核源码里的:Documentation/devicetree/bindings/leds/leds-gpio.txt

<think>好的,我现在要回答用户关于Kbuild语法中obj-$(CONFIG_LEDS_GPIO)的含义及其在Makefile中的使用的问题。首先,我需要回忆一下Kbuild系统的基本知识,以及用户提供的引用内容,然后结合这些信息来构建一个清晰的解释。 根据用户提供的引用,特别是引用[2],Kbuild Makefile的核心包括目标定义,用于决定哪些内容编译为模块或内置到内核。这提到的obj-$(CONFIG_...)结构应该是用来定义这些目标的。用户的问题是关于obj-$(CONFIG_LEDS_GPIO)的具体含义,所以我需要解释这个语法的作用和CONFIG_LEDS_GPIO的作用。 首先,CONFIG_LEDS_GPIO是一个配置选项,通常在.config文件中设置,可能为y(内置)、m(模块)或未定义(不编译)。当用户执行make menuconfig或其他配置工具时,这个选项的状态会被确定下来。在Makefile中使用obj-$(CONFIG_LEDS_GPIO)时,会根据该配置选项的值展开为obj-y、obj-m或空。 例如,如果CONFIG_LEDS_GPIO设置为y,那么obj-$(CONFIG_LEDS_GPIO)就变成obj-y,对应的目标文件会被编译并链接进内核。如果是m,则变成obj-m,生成可加载的模块。如果未设置,则该行无效,不进行编译。 接下来,我需要确认是否正确理解了Kbuild的变量展开机制。根据引用[3]中的例子,machine-$(CONFIG_ARCH_EXYNOS) := exynos,当CONFIG_ARCH_EXYNOS设置为y时,变量machine-y被赋值为exynos,这用于体系结构相关的Makefile。同样的逻辑适用于obj-$,因此obj-$(CONFIG_LEDS_GPIO)会根据配置展开,并决定如何编译对应的模块或内置代码。 此外,引用[2]提到目标定义是Kbuild Makefile的核心内容之一,所以obj-$(CONFIG_LEDS_GPIO)的作用就是定义需要构建的目标,具体取决于配置选项的值。这可能涉及到一个或多个源文件,例如obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o,这样leds-gpio.c会被编译为对应的目标文件,并根据配置选项决定其构建方式。 需要确保解释清楚变量展开后的结果以及不同情况下的行为,比如y、m、未定义的区别,并举例说明。同时,结合用户提供的引用,特别是引用[2]和[3],以增强回答的可信度。 可能还需要提到,当配置选项为m时,除了生成模块文件外,还需要处理模块的依赖关系,但用户的问题可能不需要深入到这一层面,保持回答简洁即可。 总结来说,回答的结构应该包括: 1. 解释obj-$(...)语法的作用,根据配置变量展开为obj-y或obj-m。 2. 说明CONFIG_LEDS_GPIO的可能取值及其影响。 3. 提供一个具体的例子,如obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o,并解释不同配置下的行为。 4. 引用用户提供的参考资料来支持解释,如引用[2]和[3]。 最后,生成的相关问题需要围绕Kbuild语法、配置选项的使用、模块编译与内置的区别等方面展开,帮助用户进一步探索相关知识。</think>### Kbuild语法中`obj-$(CONFIG_LEDS_GPIO)`的含义与用法 在Kbuild系统中,`obj-$(CONFIG_LEDS_GPIO)` 是一种动态变量定义语法,用于根据内核配置选项 `CONFIG_LEDS_GPIO` 的值(`y`、`m`或未定义)决定是否将某个目标文件编译进内核、编译为模块,或完全忽略。 #### 1. 语法解析 - **`obj-$(...)`结构**: 这的 `$(...)` 表示变量展开。`CONFIG_LEDS_GPIO` 是内核配置工具(如 `make menuconfig`)生成的配置选项,其值可能是: - `y`(yes):将目标文件编译**进内核镜像**(built-in)。 - `m`(module):将目标文件编译**为可加载模块**(.ko文件)。 - **未定义**:不编译该目标文件。 例如: ```makefile obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o ``` - 若 `CONFIG_LEDS_GPIO=y`,则展开为 `obj-y += leds-gpio.o`,表示将 `leds-gpio.o` 编译进内核- 若 `CONFIG_LEDS_GPIO=m`,则展开为 `obj-m += leds-gpio.o`,表示生成 `leds-gpio.ko` 模块。 #### 2. `CONFIG_LEDS_GPIO` 的作用 - **功能关联**: `CONFIG_LEDS_GPIO` 通常用于控制是否支持**通过GPIO引脚控制的LED设备驱动**。当用户在内核配置中启用此选项时(设为 `y` 或 `m`),对应的驱动代码会被编译[^2]。 - **依赖关系**: 该选项可能依赖其他配置,例如 `CONFIG_LEDS_CLASS`(LED设备框架)或 `CONFIG_GPIOLIB`(GPIO库支持),这些依赖关系在 `Kconfig` 文件中定义。 #### 3. 实际应用示例 假设驱动代码文件为 `leds-gpio.c`,在Makefile中定义: ```makefile obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o ``` - **编译进内核**(`y`): 最终内核镜像包含此驱动,系统启动时自动支持GPIO LED- **编译为模块**(`m`): 生成 `leds-gpio.ko` 文件,可通过 `insmod leds-gpio.ko` 动态加载。 - **未启用**: 该驱动不会被编译,减少内核体积。 #### 4. 与体系结构Makefile的关联 体系结构相关的Makefile(如 `/arch/arm/Makefile`)可能通过类似语法定义平台支持。例如: ```makefile machine-$(CONFIG_ARCH_EXYNOS) := exynos ``` 当 `CONFIG_ARCH_EXYNOS=y` 时,变量 `machine-y` 被赋值为 `exynos`,指定编译时使用Exynos平台相关代码[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值