Linux 内核已经自带了LED 灯驱动,要使用 Linux 内核自带的 LED 灯驱动首先配置 Linux 内核。
内核配置:
-> Device Drivers
-> LED Support (NEW_LEDS [=y])
->LED Support for GPIO connected LEDs
源文件路径:drivers/leds/leds-gpio.c
static const struct of_device_id of_gpio_leds_match[] = {
{ .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,
},
};
compatible 内容为“gpio-leds”,设备树中的 LED 灯设备节点的 compatible 属性值也要为“gpio-leds”
Linux 内核自带的 LED 驱动采用了 platform 框架,匹配成功后gpio_led_probe 函数就会执行。
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) {
priv = devm_kzalloc(&pdev->dev,
sizeof_gpio_leds_priv(pdata->num_leds),
GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->num_leds = pdata->num_leds;
for (i = 0; i < priv->num_leds; i++) {
ret = create_gpio_led(&pdata->leds[i],
&priv->leds[i],
&pdev->dev, pdata->gpio_blink_set);
if (ret < 0) {
/* On failure: unwind the led creations */
for (i = i - 1; i >= 0; i--)
delete_gpio_led(&priv->leds[i]);
return ret;
}
}
} else {
priv = gpio_leds_create(pdev);
if (IS_ERR(priv))
return PTR_ERR(priv);
}
platform_set_drvdata(pdev, priv);
return 0;
}
pdata是通过dev_get_platdata()获取到的该平台设备的私有数据,由于没有平台私有数据因此pdata为空,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(pdev);
if (IS_ERR(priv))
return PTR_ERR(priv);
}
platform_set_drvdata(pdev, priv);
return 0;
}
gpio_leds_create函数
static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct fwnode_handle *child;
struct gpio_leds_priv *priv;
int count, ret;
struct device_node *np;
count = device_get_child_node_count(dev);
if (!count)
return ERR_PTR(-ENODEV);
priv = devm_kzalloc(dev, sizeof_gpio_leds_priv(count), GFP_KERNEL);
if (!priv)
return ERR_PTR(-ENOMEM);
device_for_each_child_node(dev, child) {
struct gpio_led led = {};
const char *state = NULL;
led.gpiod = devm_get_gpiod_from_child(dev, NULL, child);
if (IS_ERR(led.gpiod)) {
fwnode_handle_put(child);
ret = PTR_ERR(led.gpiod);
goto err;
}
np = of_node(child);
if (fwnode_property_present(child, "label")) {
fwnode_property_read_string(child, "label", &led.name);
} else {
if (IS_ENABLED(CONFIG_OF) && !led.name && np)
led.name = np->name;
if (!led.name)
return ERR_PTR(-EINVAL);
}
fwnode_property_read_string(child, "linux,default-trigger",
&led.default_trigger);
if (!fwnode_property_read_string(child, "default-state",
&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 (fwnode_property_present(child, "retain-state-suspended"))
led.retain_state_suspended = 1;
ret = create_gpio_led(&led, &priv->leds[priv->num_leds++],
dev, NULL);
if (ret < 0) {
fwnode_handle_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(ret);
}
1.获取LED灯子节点的数量count = device_get_child_node_count(dev)
2.遍历每一个子节点的属性device_for_each_child_node(dev, child)
3.创建每一个子节点的LED字符设备ret = create_gpio_led(&led)
设备树
dtsleds {
compatible = "gpio-leds";
led0 {
label = "red";
gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
default-state = "off";
};
led1 {
label = "green";
gpios = <&gpio1 4 GPIO_ACTIVE_LOW>;
default-state = "off";
};
led2 {
label = "blue";
gpios = <&gpio1 5 GPIO_ACTIVE_LOW>;
default-state = "off";
};
};
如果内核跑起来/sys/class/leds/路径下没有出现对应的LED目录有可能是虽然平台驱动匹配成功了,但是由于LED的GPIO已经被其它驱动占用了,IO申请不成功导致平台驱动注册失败,请注意排查设备树的有没有IO处突
LED操作
//开灯
echo 1 > /sys/class/leds/red/brightness
//关灯
echo 0 > /sys/class/leds/red/brightness