1. 车载Linux内核模块最小骨架搭建
任务目标
在车载Linux开发板上实现一个GPIO LED字符设备驱动,通过sysfs接口控制LED的亮灭。驱动注册后,用户可通过/sys/class/leds/car_led/brightness写入0或1来控制LED状态。
环境与依赖
- Linux内核源码树(已配置交叉编译环境)
- 开发板:任意带GPIO引脚的ARM开发板(本文以GPIO编号49为例)
- 内核配置需开启:
CONFIG_LEDS_CLASS、CONFIG_LEDS_GPIO
驱动实现
使用Linux内核LED子系统(leds-class-gpio),驱动只需定义GPIO引脚、注册LED设备,无需手动操作字符设备或实现sysfs属性——子系统自动生成brightness等属性文件。
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#define DRIVER_NAME "car_gpio_led"
struct gpio_led_data {
struct led_classdev cdev;
int gpio;
};
static void car_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct gpio_led_data *led_data =
container_of(led_cdev, struct gpio_led_data, cdev);
gpio_set_value(led_data->gpio, value ? 1 : 0);
}
static int gpio_led_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct gpio_led_data *led_data;
enum of_gpio_flags flags;
int ret;
led_data = devm_kzalloc(&pdev->dev, sizeof(*led_data), GFP_KERNEL);
if (!led_data)
return -ENOMEM;
led_data->gpio = of_get_named_gpio_flags(np, "gpios", 0, &flags);
if (!gpio_is_valid(led_data->gpio)) {
dev_err(&pdev->dev, "invalid GPIO\n");
return -EINVAL;
}
ret = devm_gpio_request_one(&pdev->dev, led_data->gpio,
GPIOF_OUT_INIT_LOW, "car_led");
if (ret < 0)
return ret;
led_data->cdev.name = "car_led";
led_data->cdev.brightness_set = car_led_set;
led_data->cdev.max_brightness = 1;
ret = devm_led_classdev_register(&pdev->dev, &led_data->cdev);
if (ret < 0) {
dev_err(&pdev->dev, "failed to register LED\n");
return ret;
}
platform_set_drvdata(pdev, led_data);
dev_info(&pdev->dev, "car LED registered on GPIO %d\n",
led_data->gpio);
return 0;
}
static const struct of_device_id gpio_led_of_match[] = {
{ .compatible = "vendor,car-gpio-led" },
{ }
};
MODULE_DEVICE_TABLE(of, gpio_led_of_match);
static struct platform_driver gpio_led_driver = {
.probe = gpio_led_probe,
.driver = {
.name = DRIVER_NAME,
.of_match_table = gpio_led_of_match,
},
};
module_platform_driver(gpio_led_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AutoSAR Lab");
MODULE_DESCRIPTION("Car GPIO LED driver");
设备树配置
在开发板设备树源文件(.dts)的根节点下添加LED节点:
/ {
car_led {
compatible = "vendor,car-gpio-led";
gpios = <&gpio0 17 GPIO_ACTIVE_HIGH>; /* GPIO 49 = bank0 pin17 */
};
};
编译与加载
- 将驱动源码放入内核
drivers/leds/目录,并在该目录Makefile添加:obj-$(CONFIG_LEDS_GPIO) += car_gpio_led.o - 编译内核或单独编译模块:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules - 将生成的
car_gpio_led.ko拷贝到开发板,加载:insmod car_gpio_led.ko
验证方法
- 加载驱动后检查设备节点:
ls /sys/class/leds/,应看到car_led目录 - 点亮LED:
echo 1 > /sys/class/leds/car_led/brightness,观察LED灯亮起 - 熄灭LED:
echo 0 > /sys/class/leds/car_led/brightness,观察LED灯熄灭 - 读取当前状态:
cat /sys/class/leds/car_led/brightness,输出应为最后写入的0或1
通过条件:写入1时LED物理点亮,写入0时熄灭,且读取值与写入值一致。若LED无反应,用dmesg | tail查看驱动日志确认probe是否成功及GPIO号是否正确。