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 节点即可
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驱动。
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,设置为输出等。
调用 device_get_child_node_count 函数统计子节点数量,一般在在设备树中创建一个节点表示 LED 灯,然后在这个节点下面为每个 LED 灯创建一个子节点。因此子节点数量也是 LED 灯的数量。
调用 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";
};
};
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