27. Глава 5.26: Подключение RGB LED к LED class через DT и кастомный драйвер
27.1. Цель
Подключить RGB-светодиод к подсистеме LED class в Linux без указания GPIO в Device Tree, используя кастомный драйвер, в котором все GPIO определяются внутри кода.
27.2. Исходный код драйвера с комментариями
1#include <linux/module.h>
2#include <linux/init.h>
3#include <linux/of.h>
4#include <linux/of_device.h>
5#include <linux/platform_device.h>
6#include <linux/leds.h>
7#include <linux/gpio.h>
8#include <linux/gpio/consumer.h>
9#include <linux/slab.h>
10#include <linux/kernel.h>
11#include <linux/types.h>
12
13#define MAX_LEDS 3
14
15struct rgb_led {
16 struct led_classdev cdev;
17 int gpio;
18};
19
20struct rgb_led_data {
21 struct rgb_led *leds[MAX_LEDS];
22 int count;
23};
24
25static void rgb_brightness_set(struct led_classdev *cdev, enum led_brightness brightness)
26{
27 struct rgb_led *led = container_of(cdev, struct rgb_led, cdev);
28 gpio_set_value_cansleep(led->gpio, brightness ? 1 : 0);
29}
30
31static int rgb_led_probe(struct platform_device *pdev)
32{
33 struct device_node *np = pdev->dev.of_node;
34 struct device_node *child;
35 struct rgb_led_data *data;
36 const char *label;
37 int ret, i;
38 int gpios[] = { 16, 20, 21 }; // RED, GREEN, BLUE
39
40 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
41 if (!data)
42 return -ENOMEM;
43
44 i = 0;
45 for_each_child_of_node(np, child) {
46 struct rgb_led *led;
47
48 if (i >= MAX_LEDS)
49 break;
50
51 led = kzalloc(sizeof(*led), GFP_KERNEL);
52 if (!led)
53 return -ENOMEM;
54
55 led->gpio = gpios[i];
56
57 ret = gpio_request(led->gpio, "rgb-led");
58 if (ret) {
59 dev_err(&pdev->dev, "Failed to request GPIO %d\n", led->gpio);
60 kfree(led);
61 return ret;
62 }
63
64 gpio_direction_output(led->gpio, 0);
65
66 if (of_property_read_string(child, "label", &label))
67 label = "rgb:unknown";
68
69 led->cdev.name = kstrdup(label, GFP_KERNEL);
70 led->cdev.brightness = LED_OFF;
71 led->cdev.max_brightness = 1;
72 led->cdev.brightness_set = rgb_brightness_set;
73
74 ret = led_classdev_register(&pdev->dev, &led->cdev);
75 if (ret) {
76 dev_err(&pdev->dev, "Failed to register LED %s\n", label);
77 gpio_set_value(led->gpio, 0);
78 gpio_free(led->gpio);
79 kfree(led->cdev.name);
80 kfree(led);
81 return ret;
82 }
83
84 dev_info(&pdev->dev, "Registered LED: %s on GPIO %d\n", label, led->gpio);
85 data->leds[i++] = led;
86 }
87
88 data->count = i;
89 platform_set_drvdata(pdev, data);
90
91 dev_info(&pdev->dev, "RGB LED driver initialized with %d LED(s)\n", data->count);
92 return 0;
93}
94
95static int rgb_led_remove(struct platform_device *pdev)
96{
97 struct rgb_led_data *data = platform_get_drvdata(pdev);
98 int i;
99
100 for (i = 0; i < data->count; i++) {
101 struct rgb_led *led = data->leds[i];
102 if (!led)
103 continue;
104
105 pr_info("Unregistering LED: %s from GPIO %d\n", led->cdev.name, led->gpio);
106 led_classdev_unregister(&led->cdev);
107 gpio_set_value(led->gpio, 0);
108 gpio_free(led->gpio);
109 kfree(led->cdev.name);
110 kfree(led);
111 }
112
113 pr_info("RGB LED driver removed\n");
114 return 0;
115}
116
117static const struct of_device_id rgb_led_of_match[] = {
118 { .compatible = "myvendor,rgb-led" },
119 { /* sentinel */ }
120};
121MODULE_DEVICE_TABLE(of, rgb_led_of_match);
122
123static struct platform_driver rgb_led_driver = {
124 .driver = {
125 .name = "rgb-led",
126 .of_match_table = rgb_led_of_match,
127 },
128 .probe = rgb_led_probe,
129 .remove = rgb_led_remove,
130};
131
132module_platform_driver(rgb_led_driver);
133
134MODULE_LICENSE("GPL");
135MODULE_AUTHOR("Your Name");
136MODULE_DESCRIPTION("Reloadable RGB LED driver with dmesg logging");
—
27.3. Фрагмент Device Tree
rgb-led@0 {
compatible = "myvendor,rgb-led";
status = "okay";
red {
label = "rgb:red";
};
green {
label = "rgb:green";
};
blue {
label = "rgb:blue";
};
};
Комментарии:
Описывается один узел rgb-led@0 с подузлами red, green, blue
Пины GPIO в DT не указываются
Все LED автоматически получают label и отображаются в /sys/class/leds/
27.4. Проверка результата
# Проверка устройств LED class
ls /sys/class/leds/
# → должны появиться:
# rgb:red rgb:green rgb:blue
# Включение красного светодиода
echo 1 | sudo tee /sys/class/leds/rgb:red/brightness
# Выключение синего
echo 0 | sudo tee /sys/class/leds/rgb:blue/brightness
# Проверка, что узел в DT активен
ls /proc/device-tree/soc/rgb-led@0/
# → red green blue compatible status ...
27.5. Вывод
Мы реализовали минимальный, понятный, переносимый драйвер для RGB LED, где вся логика и GPIO-привязка находятся внутри модуля. Управление выполняется через стандартный LED class интерфейс, без использования устаревшего sysfs GPIO или userspace-демонов.