当前位置: 首页 > article >正文

LED子系统

LED子系统 框架

LED子系统是Linux内核中的一个子系统,用于驱动LED灯。它提供了一套标准的驱动框架,使得开发人员能够很容易地开发LED驱动程序,并且不用关心底层硬件细节。

LED子系统的核心是一个驱动模型,驱动模型为LED提供了一套标准的API,包括LED的开启、关闭、闪烁等操作。驱动模型还提供了一个标准的设备树节点格式,允许开发人员通过设备树来描述硬件信息。

LED子系统中的驱动程序可以分为两类:LED Class驱动和Platform驱动。LED Class驱动是一种通用的LED驱动程序,它可以适用于多种不同的硬件平台和LED类型。Platform驱动则是特定于某一硬件平台的驱动程序,它依赖于特定的硬件平台,需要针对不同的硬件平台进行编写。

在编写LED驱动程序时,开发人员需要实现LED子系统提供的一组标准接口,包括probe、remove、set_brightness等接口。其中,probe接口用于驱动初始化,remove接口用于驱动卸载,set_brightness接口用于设置LED的亮度。开发人员还需要在设备树中描述LED的硬件信息,包括GPIO引脚、亮度范围等。

总之,LED子系统提供了一套标准的驱动框架,使得开发人员能够很容易地开发LED驱动程序,并且不用关心底层硬件细节。

例子

设备树

led@0 {
    compatible = "gpio-leds";
    label = "LED";
    gpios = <&gpio1 16 GPIO_ACTIVE_LOW>;
    default-state = "off";
};

linux 驱动

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/gpio.h>

#define LED_NAME    "led"
#define LED_DEFAULT_TRIGGER "heartbeat"

struct led_info {
    struct led_classdev cdev;
    int gpio;
};

static int led_probe(struct platform_device *pdev)
{
    struct led_info *led;
    struct device_node *node = pdev->dev.of_node;
    const char *name = node->name;
    const char *trigger = LED_DEFAULT_TRIGGER;
    int ret;

    led = devm_kzalloc(&pdev->dev, sizeof(struct led_info), GFP_KERNEL);
    if (!led)
        return -ENOMEM;

    ret = of_property_read_string(node, "default-state", &led->cdev.default_trigger);
    if (ret) {
        dev_err(&pdev->dev, "failed to read default-state: %d\n", ret);
        return ret;
    }

    ret = of_property_read_string(node, "default-trigger", &trigger);
    if (ret) {
        dev_err(&pdev->dev, "failed to read default-trigger: %d\n", ret);
        return ret;
    }

    led->gpio = of_get_gpio_flags(node, 0, NULL);
    if (led->gpio < 0) {
        dev_err(&pdev->dev, "failed to get gpio: %d\n", led->gpio);
        return led->gpio;
    }

    led->cdev.name = name;
    led->cdev.brightness_set = led_brightness_set;
    led->cdev.brightness = LED_OFF;
    led->cdev.max_brightness = LED_FULL;

    ret = devm_led_classdev_register(&pdev->dev, &led->cdev);
    if (ret) {
        dev_err(&pdev->dev, "failed to register led classdev: %d\n", ret);
        return ret;
    }

    led_trigger_register_simple(name, &led->cdev, trigger);

    platform_set_drvdata(pdev, led);

    dev_info(&pdev->dev, "LED driver initialized\n");

    return 0;
}

static int led_remove(struct platform_device *pdev)
{
    struct led_info *led = platform_get_drvdata(pdev);

    led_trigger_unregister_simple(&led->cdev);
    devm_led_classdev_unregister(&pdev->dev, &led->cdev);

    return 0;
}

static const struct of_device_id my_led_dt_ids[] = {
{ .compatible = "my_leds" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_led_dt_ids);

static struct platform_driver my_led_driver = {
.probe = my_led_probe,
.remove = my_led_remove,
.driver = {
.name = "my_led",
.owner = THIS_MODULE,
.of_match_table = my_led_dt_ids,
},
};

module_platform_driver(my_led_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your name");
MODULE_DESCRIPTION("My LED driver");
MODULE_ALIAS("platform:my_led");

注意,上述代码中的 “my_led” 为驱动程序的名字,“my_leds” 是设备树中该设备节点的 compatible 属性值,需要保持一致。

下面是一个简单的应用程序示例,它可以控制 LED 灯的开关状态,利用了 LED 子系统提供的 sysfs 接口:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define LED_PATH "/sys/class/leds/my_led"

int main(int argc, char *argv[])
{
    int fd;
    char buf[1024];

    if (argc < 2) {
        printf("Usage: %s on|off\n", argv[0]);
        return -1;
    }

    fd = open(LED_PATH "/brightness", O_WRONLY);
    if (fd < 0) {
        printf("Failed to open %s\n", LED_PATH "/brightness");
        return -1;
    }

    if (strcmp(argv[1], "on") == 0) {
        strcpy(buf, "1\n");
    } else if (strcmp(argv[1], "off") == 0) {
        strcpy(buf, "0\n");
    } else {
        printf("Invalid command %s\n", argv[1]);
        close(fd);
        return -1;
    }

    if (write(fd, buf, strlen(buf)) != strlen(buf)) {
        printf("Failed to write %s\n", LED_PATH "/brightness");
        close(fd);
        return -1;
    }

    close(fd);
    return 0;
}

下面是带有echo 接口的驱动例子

#include <linux/module.h>
#include <linux/init.h>
#include <linux/leds.h>
#include <linux/platform_device.h>

static struct led_classdev my_led;

static ssize_t my_led_brightness_show(struct device *dev,
                                       struct device_attribute *attr,
                                       char *buf)
{
    int brightness = my_led.brightness;

    return sprintf(buf, "%d\n", brightness);
}

static ssize_t my_led_brightness_store(struct device *dev,
                                        struct device_attribute *attr,
                                        const char *buf, size_t count)
{
    int brightness;

    sscanf(buf, "%d", &brightness);
    if (brightness < 0)
        brightness = 0;
    else if (brightness > 255)
        brightness = 255;

    my_led.brightness = brightness;
    led_set_brightness(&my_led, brightness);

    return count;
}

static DEVICE_ATTR(brightness, S_IRUGO | S_IWUSR,
                    my_led_brightness_show, my_led_brightness_store);

static int my_led_probe(struct platform_device *pdev)
{
    struct resource *res;
    int err;

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res) {
        dev_err(&pdev->dev, "Failed to get memory resource\n");
        return -EINVAL;
    }

    my_led.name = "my_led";
    my_led.brightness = 0;
    my_led.max_brightness = 255;
    my_led.blink_brightness = 0;
    my_led.blink_delay_on = 100;
    my_led.blink_delay_off = 100;

    err = led_classdev_register(&pdev->dev, &my_led);
    if (err < 0) {
        dev_err(&pdev->dev, "Failed to register LED device\n");
        return err;
    }

    err = device_create_file(&pdev->dev, &dev_attr_brightness);
    if (err < 0) {
        dev_err(&pdev->dev, "Failed to create sysfs file\n");
        led_classdev_unregister(&my_led);
        return err;
    }

    dev_info(&pdev->dev, "LED driver initialized\n");

    return 0;
}

static int my_led_remove(struct platform_device *pdev)
{
    device_remove_file(&pdev->dev, &dev_attr_brightness);
    led_classdev_unregister(&my_led);
    return 0;
}

static const struct of_device_id my_led_of_match[] = {
    { .compatible = "virtual,my_led", },
    {},
};
MODULE_DEVICE_TABLE(of, my_led_of_match);

static struct platform_driver my_led_driver = {
    .probe = my_led_probe,
    .remove = my_led_remove,
    .driver = {
        .name = "my_led",
        .of_match_table = my_led_of_match,
        .owner = THIS_MODULE,
    },
};

module_platform_driver(my_led_driver);

MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("LED driver with read/write operations");
MODULE_LICENSE("GPL");

下面是它的设备树

/dts-v1/;
 
/ {
    model = "my_board";
    compatible = "my_board";
 
    my_led {
        compatible = "virtual,my_led";
        reg = <0x1000>;
        brightness = <0>;
    };
};

下面是一个简单的 LED 子系统驱动程序示例,它利用了 sysfs 接口来控制 LED 灯的开关状态:

驱动代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

#define DRIVER_NAME "myled"
#define LED_CLASS_NAME "my_led"
#define LED_NAME "my_led"
#define LED_PATH "/sys/class/leds/my_led/brightness"

static struct class *led_class;
static struct device *led_device;

static ssize_t myled_read(struct file *filep, char __user *buf, size_t count, loff_t *f_pos)
{
    int fd, ret;
    char led_status;

    fd = open(LED_PATH, O_RDONLY);
    if (fd < 0) {
        pr_err("Failed to open %s\n", LED_PATH);
        return -EINVAL;
    }

    ret = read(fd, &led_status, 1);
    if (ret < 0) {
        pr_err("Failed to read %s\n", LED_PATH);
        close(fd);
        return -EINVAL;
    }

    close(fd);

    if (copy_to_user(buf, &led_status, 1)) {
        pr_err("Failed to copy data to user\n");
        return -EFAULT;
    }

    return 1;
}

static ssize_t myled_write(struct file *filep, const char __user *buf, size_t count, loff_t *f_pos)
{
    int fd, ret;
    char led_status;

    if (copy_from_user(&led_status, buf, 1)) {
        pr_err("Failed to copy data from user\n");
        return -EFAULT;
    }

    fd = open(LED_PATH, O_WRONLY);
    if (fd < 0) {
        pr_err("Failed to open %s\n", LED_PATH);
        return -EINVAL;
    }

    ret = write(fd, &led_status, 1);
    if (ret < 0) {
        pr_err("Failed to write %s\n", LED_PATH);
        close(fd);
        return -EINVAL;
    }

    close(fd);

    return 1;
}

static struct file_operations myled_fops = {
    .owner = THIS_MODULE,
    .read = myled_read,
    .write = myled_write,
};

static int myled_init(void)
{
    int ret;

    ret = register_chrdev(0, DRIVER_NAME, &myled_fops);
    if (ret < 0) {
        pr_err("Failed to register char device\n");
        return ret;
    }

    led_class = class_create(THIS_MODULE, LED_CLASS_NAME);
    if (IS_ERR(led_class)) {
        pr_err("Failed to create LED class\n");
        unregister_chrdev(ret, DRIVER_NAME);
        return PTR_ERR(led_class);
    }

    led_device = device_create(led_class, NULL, MKDEV(ret, 0), NULL, LED_NAME);
    if (IS_ERR(led_device)) {
        pr_err("Failed to create LED device\n");
        class_destroy(led_class);
        unregister_chrdev(ret, DRIVER_NAME);
        return PTR_ERR(led_device);
    }

    return 0;
}

static void myled_exit(void)
{
    device_destroy(led_class, MKDEV(0, 0));
    class_unregister(led_class);
    class_destroy(led_class);
    unregister_chrdev(0, DRIVER_NAME);
}

module_init(myled_init);
module_exit(myled_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("

DEVICE_ATTR是用于创建一个sysfs设备文件节点的宏定义,可以通过该节点访问设备的属性值。该宏定义的语法如下:

scss

DEVICE_ATTR(name, mode, show, store)

name: 属性名称,字符串类型,必选参数。
mode: 属性的访问权限,属性值为S_IRUGO表示只读,属性值为S_IRUGO | S_IWUSR表示读写,属性值为S_IWUSR表示只写,可选参数。
show: 指向show函数的指针,用于读取属性值,函数返回值为属性值的长度,必选参数。
store: 指向store函数的指针,用于写入属性值,函数返回值为写入的属性值的长度,必须有读写属性才需要这个参数。

可以加入用DEVICE_ATTR 节点

以下是一个示例:

static ssize_t led_state_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    int state;
    struct led_dev *led = dev_get_drvdata(dev);

    state = gpio_get_value(led->gpio_pin);
    return scnprintf(buf, PAGE_SIZE, "%d\n", state);
}

static ssize_t led_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
    int state;
    struct led_dev *led = dev_get_drvdata(dev);

    sscanf(buf, "%d", &state);
    gpio_set_value(led->gpio_pin, state);
    return count;
}

static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, led_state_show, led_state_store);

这里定义了一个名为state的属性,它的访问权限为可读可写,show函数为led_state_show,store函数为led_state_store。在应用层可以通过/sys/class/leds/led1/state文件节点来读写LED状态。


http://www.kler.cn/a/15685.html

相关文章:

  • 为正在运行的 Docker 容器重启策略,以提高服务的可用性
  • Android CALL按键同步切换通话界面上免提和听筒的图标显示
  • vue之axios根据某个接口创建实例,并设置headers和超时时间,捕捉异常
  • 【SpringBoot】公共字段自动填充
  • vue3 element el-table实现表格动态增加/删除/编辑表格行,带有校验规则
  • Azure pipeline 通过git命令修改文件
  • C. Multiplicity(DP + 分解因数)
  • Java多线程中Lock的使用
  • 9.pytorch lightning之数据模块LightningDataModule
  • mysql笔记
  • python实战应用讲解-【numpy数组篇】常用函数(六)(附python示例代码)
  • HPC的资料
  • 【Docker】Dockerfile简介
  • 被修饰成单栋的倾斜摄影处理思路
  • 有理函数的不定积分
  • 《港联证券》半导体复苏预期“抢跑”产业现实 细分市场缓慢回温
  • ETL工具 - Kettle 转换算子介绍
  • Linux进程间通信 - 信号(signal) 与 管道(pipe) 与 消息队列
  • 【VM服务管家】VM4.0软件使用_1.1 环境配置类
  • SpringBoot 中 4 种常用的数据库访问方式
  • Microsoft Bitlocker企业级管理部署方案
  • 在京东工作8年的程序员,35岁被裁拿到30多万的赔偿,终于自由了
  • 2023天梯赛补题
  • 华为OD机试 - 模拟商场优惠打折(Python)
  • 回溯算法秒杀2
  • c++算法——vector