Linux 内核自带的 LED 灯驱动

Posted 九章_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux 内核自带的 LED 灯驱动相关的知识,希望对你有一定的参考价值。

系列文章

I.MX6ULL 手册查找使用方法 实战点亮LED(寄存器版)
I.MX6ULL 手册查找使用方法 实战点亮LED(固件库版本)
linux 字符设备驱动实战
linux LED设备驱动文件
linux 设备树(.dts)实战解析
linux 使用设备树点亮LED 实战
linux 驱动中并发与竞争
linux 内核定时器
linux 内核中断理解
linux 驱动阻塞和非阻塞
linux 内核异步通知
linux platform驱动框架

前言

学习如何使用 Linux 内核自带的 LED 驱动来驱动 I.MX6U-ALPHA 开发板上的 LED0。
LED 灯这样非常基础的设备驱动, Linux 内核已经集成了。 Linux 内核的 LED 灯驱动采用 platform 框架。因此我们只需要按照要求在设备树文件中添加相应的 LED 节点即可

linux 自带LED驱动理解

1、内核配置:

-> Device Drivers
	-> LED Support (NEW_LEDS [=y])
		->LED Support for GPIO connected LEDs

在这里插入图片描述

选择“LED Support for GPIO connected LEDs”,将其编译进 Linux 内核

LED 灯驱动框架分析
源文件路径:drivers/leds/leds-gpio.c

static const struct of_device_id of_gpio_leds_match[] = {
{ .compatible = "gpio-leds", },
 {},
};

compatible 内容为“gpio-leds”,设备树中的 LED 灯设备节点的 compatible 属性值也要为“gpio-leds”

static struct platform_driver gpio_led_driver = {
	.probe = gpio_led_probe,
	.remove = gpio_led_remove,
	.driver = {
	.name = "leds-gpio",
	 .of_match_table = of_gpio_leds_match,
	},
};
module_platform_driver(gpio_led_driver);

Linux 内核自带的 LED 驱动采用了 platform 框架,匹配成功后gpio_led_probe 函数就会执行。通过 module_platform_driver 函数向 Linux 内核注册 gpio_led_driver 这个 platform驱动。

gpio_led_probe 函数

static int gpio_led_probe(struct platform_device *pdev)
{
        struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
        struct gpio_leds_priv *priv;
        int i, ret = 0;

        if (pdata && pdata->num_leds) {
             ... 
        } else {
                priv = gpio_leds_create_of(pdev);
                if (IS_ERR(priv))
                        return PTR_ERR(priv);
        }

        platform_set_drvdata(pdev, priv);

        return 0;
}

使用 gpio_leds_create 函数从设备树中提取设备信息,gpio_leds_create 函数如下

static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
{
        struct device_node *np = pdev->dev.of_node, *child;
        struct gpio_leds_priv *priv;
        int count, ret;

        /* count LEDs in this device, so we know how much to allocate */
        count = of_get_available_child_count(np);
        if (!count)
                return ERR_PTR(-ENODEV);
		//遍历每个子节点,获取每个子节点的信息。
        for_each_available_child_of_node(np, child)
                if (of_get_gpio(child, 0) == -EPROBE_DEFER)
                        return ERR_PTR(-EPROBE_DEFER);

        priv = devm_kzalloc(&pdev->dev, sizeof_gpio_leds_priv(count),
                        GFP_KERNEL);
        if (!priv)
                return ERR_PTR(-ENOMEM);
		//获取 LED 灯所使用的 GPIO 信息
        for_each_available_child_of_node(np, child) {
                struct gpio_led led = {};
                enum of_gpio_flags flags;
                const char *state;

                led.gpio = of_get_gpio_flags(child, 0, &flags);
                led.active_low = flags & OF_GPIO_ACTIVE_LOW;
                //读取子节点 label 属性值
                led.name = of_get_property(child, "label", NULL) ? : child->name;
                //获取“linux,default-trigger”属性值
                led.default_trigger =of_get_property(child, "linux,default-trigger", NULL);
                 //获取“default-state”属性值
                state = of_get_property(child, "default-state", NULL);
                if (state) {
                        if (!strcmp(state, "keep"))
                                led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
                        else if (!strcmp(state, "on"))
                                led.default_state = LEDS_GPIO_DEFSTATE_ON;
                        else
                                led.default_state = LEDS_GPIO_DEFSTATE_OFF;
                }
                if (of_get_property(child, "retain-state-suspended", NULL))
                        led.retain_state_suspended = 1;
                        
				//调用 create_gpio_led 函数创建 LED 相关的 io
                ret = create_gpio_led(&led, &priv->leds[priv->num_leds++],
                                      &pdev->dev, NULL);
                if (ret < 0) {
                        of_node_put(child);
                        goto err;
                }
        }
        return priv;
err:
        for (count = priv->num_leds - 2; count >= 0; count--)
                delete_gpio_led(&priv->leds[count]);
        return ERR_PTR(-ENODEV);
}

static const struct of_device_id of_gpio_leds_match[] = {
        { .compatible = "gpio-leds", },
        {},
};

关于 gpio_led_probe 函数就分析到这里, gpio_led_probe 函数主要功能就是获取 LED 灯的设备信息,然后根据这些信息来初始化对应的 IO,设置为输出等。

  1. 调用 device_get_child_node_count 函数统计子节点数量,一般在在设备树中创建一个节点表示 LED 灯,然后在这个节点下面为每个 LED 灯创建一个子节点。因此子节点数量也是 LED 灯的数量。

  2. 调用 create_gpio_led 函数创建 LED 相关的 io,其实就是设置 LED 所使用的 io为输出之类的。 create_gpio_led 函数主要是初始化 led_dat 这个 gpio_led_data 结构体类型变量,led_dat 保存了 LED 的操作函数等内容。

设备树节点

打开文档 Documentation/devicetree/bindings/leds/leds-gpio.txt,此文档详细的讲解了 Linux 自带驱动对应的设备树节点该如何编写。

LEDs connected to GPIO lines

Required properties:
- compatible : should be "gpio-leds".

Each LED is represented as a sub-node of the gpio-leds device.  Each
node's name represents the name of the corresponding LED.

LED sub-node properties:
- gpios :  Should specify the LED's GPIO, see "gpios property" in
  Documentation/devicetree/bindings/gpio/gpio.txt.  Active low LEDs should be
  indicated using flags in the GPIO specifier.
- label :  (optional)
  see Documentation/devicetree/bindings/leds/common.txt
- linux,default-trigger :  (optional)
  see Documentation/devicetree/bindings/leds/common.txt
- default-state:  (optional) The initial state of the LED.  Valid
  values are "on", "off", and "keep".  If the LED is already on or off
  and the default-state property is set the to same value, then no
  glitch should be produced where the LED momentarily turns off (or
  on).  The "keep" setting will keep the LED at whatever its current
  state is, without producing a glitch.  The default is off if this
  property is not present.
- retain-state-suspended: (optional) The suspend state can be retained.Such
  as charge-led gpio.

Examples:

leds {
        compatible = "gpio-leds";
        
        hdd {
                label = "IDE Activity";
                gpios = <&mcu_pio 0 1>; /* Active low */
                linux,default-trigger = "ide-disk";
        };

        fault {
                gpios = <&mcu_pio 1 0>;
                /* Keep LED on if BIOS detected hardware fault */
                default-state = "keep";
        };
};
  1. 创建一个节点表示 LED 灯设备,比如 dtsleds,如果板子上有多个 LED 灯的话每个 LED灯都作为 dtsleds 的子节点。
  2. dtsleds 节点的 compatible 属性值一定要为“gpio-leds”。
  3. 设置 label 属性,此属性为可选,每个子节点都有一个 label 属性, label 属性一般表示
    LED 灯的名字,比如以颜色区分的话就是 red、 green 等等。
  4. 每个子节点必须要设置 gpios 属性值,表示此 LED 所使用的 GPIO 引脚
  5. 设置“linux,default-trigger”属性值,也就是设置 LED 灯的默认功能,可以查阅这个文档来查看可选功能,比如:
    backlight: LED 灯作为背光。
    default-on: LED 灯打开
    heartbeat: LED 灯作为心跳指示灯,可以作为系统运行提示灯。
    ide-disk: LED 灯作为硬盘活动指示灯。
    timer: LED 灯周期性闪烁,由定时器驱动,闪烁频率可以修改
  6. 设置“default-state”属性值,可以设置为 on、 off 或 keep,为 on 的时候 LED 灯默
    认打开,为 off 的话 LED 灯默认关闭,为 keep 的话 LED 灯保持当前模式。

Documentation/devicetree/bindings/leds/common.txt

Common leds properties.

Optional properties for child nodes:
- label : The label for this LED.  If omitted, the label is
  taken from the node name (excluding the unit address).

- linux,default-trigger :  This parameter, if present, is a
    string defining the trigger assigned to the LED.  Current triggers are:
     "backlight" - LED will act as a back-light, controlled by the framebuffer
                   system
     "default-on" - LED will turn on (but for leds-gpio see "default-state"
                    property in Documentation/devicetree/bindings/gpio/led.txt)
     "heartbeat" - LED "double" flashes at a load average based rate
     "ide-disk" - LED indicates disk activity
     "timer" - LED flashes at a fixed, configurable rate

Examples:

system-status {
        label = "Status";
        linux,default-trigger = "heartbeat";
        ...
};

在 imx6ull-alientek-emmc.dts 中添加如下所示 LED 灯设备节点:

dtsleds {
	compatible = "gpio-leds";
	
	led0 {
		label = "red";
		gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
		linux,default-trigger = "heartbeat";
		default-state = "on";
	};
};

设备树的信息与驱动中gpio_leds_create_of()函数相符合。

echo 1 > /sys/class/leds/red/brightness //打开 LED0
echo 0 > /sys/class/leds/red/brightness //关闭 LED0

以上是关于Linux 内核自带的 LED 灯驱动的主要内容,如果未能解决你的问题,请参考以下文章

NanoPi NEO Air使用九:使用Linux内核自带的LED驱动

JZ2440_V3_内核驱动程序_点亮一个LED灯

i.MX6ULL驱动开发 | 09 -基于Linux自带的LED驱动点亮LED

#物联网征文#FFHHi3516DV300驱动开发——编写LED灯控制程序

LED将为我闪烁:控制发光二极管

实现点亮LED灯