LED子系统是Linux内核中有关LED的框架,开发者可以使用这个框架来进行LED设备的注册。

LED子系统程序在/driver/leds/led-class.c中

  • led_classdev结构体
    内核把LED设备的属性抽象成了一个结构体,开发人员需要填充这个结构体里面的的一些成员变量,然后注册到内核中。
struct led_classdev {const char      *name;enum led_brightness    brightness;enum led_brightness  max_brightness;int          flags;/* Lower 16 bits reflect status */
#define LED_SUSPENDED       (1 << 0)/* Upper 16 bits reflect control information */
#define LED_CORE_SUSPENDRESUME  (1 << 16)
#define LED_BLINK_ONESHOT   (1 << 17)
#define LED_BLINK_ONESHOT_STOP  (1 << 18)
#define LED_BLINK_INVERT    (1 << 19)
#define LED_SYSFS_DISABLE   (1 << 20)
#define SET_BRIGHTNESS_ASYNC    (1 << 21)
#define SET_BRIGHTNESS_SYNC (1 << 22)
#define LED_DEV_CAP_FLASH   (1 << 23)/* Set LED brightness level *//* Must not sleep, use a workqueue if needed */void        (*brightness_set)(struct led_classdev *led_cdev,enum led_brightness brightness);/** Set LED brightness level immediately - it can block the caller for* the time required for accessing a LED device register.*/int     (*brightness_set_sync)(struct led_classdev *led_cdev,enum led_brightness brightness);/* Get LED brightness level */enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);/** Activate hardware accelerated blink, delays are in milliseconds* and if both are zero then a sensible default should be chosen.* The call should adjust the timings in that case and if it can't* match the values specified exactly.* Deactivate blinking again when the brightness is set to a fixed* value via the brightness_set() callback.*/int      (*blink_set)(struct led_classdev *led_cdev,unsigned long *delay_on,unsigned long *delay_off);struct device      *dev;const struct attribute_group   **groups;struct list_head    node;          /* LED Device list */const char     *default_trigger;   /* Trigger to use */unsigned long        blink_delay_on, blink_delay_off;struct timer_list   blink_timer;int             blink_brightness;void          (*flash_resume)(struct led_classdev *led_cdev);struct work_struct   set_brightness_work;int         delayed_set_value;#ifdef CONFIG_LEDS_TRIGGERS/* Protects the trigger data below */struct rw_semaphore    trigger_lock;struct led_trigger    *trigger;struct list_head    trig_list;void         *trigger_data;/* true if activated - deactivate routine uses it to do cleanup */bool            activated;
#endif/* Ensures consistent access to the LED Flash Class device */struct mutex     led_access;
};

我们通常需要关注的以下几个成员变量
name:LED设备名字
brightness:LED的亮度
max_brightness:LED的最大亮度
brightness_set:设置LED的亮度函数
brightness_get:返回LED亮度函数

enum led_brightness {LED_OFF     = 0,LED_HALF   = 127,LED_FULL = 255,
};

led_brightness是一个枚举,里面定义了3个亮度值。灭,半亮和全亮。

  • LED框架初始化
 static int __init leds_init(void)
{leds_class = class_create(THIS_MODULE, "leds");  // 创建led类if (IS_ERR(leds_class))return PTR_ERR(leds_class);leds_class->pm = &leds_class_dev_pm_ops;leds_class->dev_groups = led_groups;      // 在leds类中创建属性return 0;
}

首先内核会先调用leds_init函数来初始化led子系统。先创建一个叫名字叫leds的类,然后在leds类中创建文件属性,这些文件属性可以在后面动态地设置LED的亮灭。

  • LED设备注册
/*** led_classdev_register - register a new object of led_classdev class.* @parent: The device to register.* @led_cdev: the led_classdev structure for this device.*/
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{char name[64];int ret;ret = led_classdev_next_name(led_cdev->name, name, sizeof(name));if (ret < 0)return ret;led_cdev->dev = device_create_with_groups(leds_class, parent, 0,led_cdev, led_cdev->groups, "%s", name); // 创建led设备if (IS_ERR(led_cdev->dev))return PTR_ERR(led_cdev->dev);if (ret)dev_warn(parent, "Led %s renamed to %s due to name collision",led_cdev->name, dev_name(led_cdev->dev));#ifdef CONFIG_LEDS_TRIGGERSinit_rwsem(&led_cdev->trigger_lock);
#endifmutex_init(&led_cdev->led_access);/* add to the list of leds */down_write(&leds_list_lock);list_add_tail(&led_cdev->node, &leds_list); // 将led设备添加内核链表up_write(&leds_list_lock);if (!led_cdev->max_brightness)  // 判断是否设备LED最大亮度led_cdev->max_brightness = LED_FULL;led_cdev->flags |= SET_BRIGHTNESS_ASYNC;led_update_brightness(led_cdev);INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed);setup_timer(&led_cdev->blink_timer, led_timer_function,(unsigned long)led_cdev);#ifdef CONFIG_LEDS_TRIGGERSled_trigger_set_default(led_cdev);
#endifdev_dbg(parent, "Registered led device: %s\n",led_cdev->name);return 0;
}

内核给驱动开发人员提供了一个LED设备注册函数,叫led_classdev_register。

  • LED设备属性文件
static ssize_t brightness_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size)
{struct led_classdev *led_cdev = dev_get_drvdata(dev);unsigned long state;ssize_t ret;mutex_lock(&led_cdev->led_access);if (led_sysfs_is_disabled(led_cdev)) {ret = -EBUSY;goto unlock;}ret = kstrtoul(buf, 10, &state);if (ret)goto unlock;if (state == LED_OFF)led_trigger_remove(led_cdev);led_set_brightness(led_cdev, state);ret = size;
unlock:mutex_unlock(&led_cdev->led_access);return ret;
}
static DEVICE_ATTR_RW(brightness);static ssize_t max_brightness_show(struct device *dev,struct device_attribute *attr, char *buf)
{struct led_classdev *led_cdev = dev_get_drvdata(dev);return sprintf(buf, "%u\n", led_cdev->max_brightness);
}
static DEVICE_ATTR_RO(max_brightness);static struct attribute *led_class_attrs[] = {&dev_attr_brightness.attr,&dev_attr_max_brightness.attr,NULL,
};static const struct attribute_group led_group = {.attrs = led_class_attrs,
};

上面几行代码会在每个led设备下创建两个设备属性文件,分别是max_brightness和brightness,用户可以直接通过读max_brightness设备文件获取LED的最大亮度,通过读写brightness设备文件来控制LED灯的亮灭。

接下来就利用led子系统写一个驱动程序来控制开发板上的LED灯,在开始写程序时要先看一下内核是否支持led子系统。
首先进入内核的图形化配置界面,在
Device Drivers
–>LED Support
–>LED Class Support
看一下LED Class Support选项是否选中,如果没有选中,就需要选中该配置项并重新编译内核代码。

如果led子系统配置成功会在/sys/class目录下看到一个叫“leds”的类。

驱动程序

struct led_dev_type
{struct miscdevice led_misc;int gpio;struct device_node* nd;  // 设备树节点struct mutex ledmutex;struct led_classdev ledclasscdev;
};struct led_dev_type ledcdev;void led_brightness_set(struct led_classdev *led_cdev,enum led_brightness brightness)
{struct led_dev_type *pdata = container_of(led_cdev, struct led_dev_type, ledclasscdev);if(LED_OFF == brightness)  // 判断用户传递参数是否为0{gpio_set_value(pdata->gpio,1);  // LED灭}else{gpio_set_value(pdata->gpio,0);  // LED亮}
}static int led_probe(struct platform_device *pdev)
{int ret = 0;DEBUG_SFLR();ledcdev.nd = pdev->dev.of_node;  // 获取设备树节点DEBUG_SFLR("ledcdev.nd -> name = %s\r\n",ledcdev.nd->name);ledcdev.gpio = of_get_named_gpio(ledcdev.nd,"led_gpio",0);  // 获取LED的GPIO号if(ledcdev.gpio < 0){ret = -1;goto end;}DEBUG_SFLR("ledcdev.gpio  = %d\r\n",ledcdev.gpio );if(gpio_is_valid(ledcdev.gpio)){ret = devm_gpio_request_one(&pdev->dev,ledcdev.gpio,GPIOF_OUT_INIT_HIGH,"atkgpioled");  // 申请GPIOif(ret < 0){ret = -1;goto end;}}else{DEBUG_SFLR("ledcdev.gpio is busy\r\n..." );ret = -1;goto end;}ledcdev.ledclasscdev.name = "imx6ull_led";  // 设置led设备名ledcdev.ledclasscdev.brightness_set  = led_brightness_set; // 设置LED控制看书devm_led_classdev_register(&pdev->dev,&ledcdev.ledclasscdev); // 注册LED设备platform_set_drvdata(pdev,&ledcdev);
end:return ret;
}static int led_remove(struct platform_device *pdev)
{// remove函数负责卸载驱动struct led_dev_type *pdata = platform_get_drvdata(pdev);devm_led_classdev_unregister(&pdev->dev,&pdata->ledclasscdev);gpio_set_value(pdata->gpio,1);gpio_free(pdata->gpio);return 0;
}
static const struct of_device_id led_match[] = {{ .compatible = "fsl,imx6ull gpioled", },{ },
};static struct platform_driver led_driver = {.probe       = led_probe,.remove     = led_remove,  // 驱动卸载时执行remove函数.driver      = {.name   = "gpioled",.of_match_table = led_match,  // 新版有设备树时通过compatible节点匹配},
};static int __init led_init_module(void)
{return platform_driver_register(&led_driver);  // 利用platform总线注册驱动
}static void __exit led_exit_module(void)
{platform_driver_unregister(&led_driver);}module_init(led_init_module);
module_exit(led_exit_module);MODULE_DESCRIPTION("xxxxxx");
MODULE_AUTHOR("xxxxx");
MODULE_LICENSE("GPL");

在程序中先申请LED灯的GPIO口,然后设置LED设备的名字以及LED灯的控制函数,然后注册LED设备。将驱动程序编译并加载到内核。

在加载驱动前,在leds类中还没有一个叫"imx6ull_led"的设备。

当驱动加载成功后,再次查看leds类中的设备,就会发现多了一个叫"imx6ull_led"的设备

进入"imx6ull_led"这个文件会发现里面有很多属性文件,我们可以读写这些属性文件来测试LED。


读取max_brightness得到LED的最大亮度。


同时也可以往brightness里面写值控制LED的亮灭。

Linux led_class子系统相关推荐

  1. 在windows10中安装 linux ubuntu 子系统

    在windows10中安装 linux ubuntu 子系统 文章目录 在windows10中安装 linux ubuntu 子系统 打开开发者模式 windows子系统功能 重启计算机 Micros ...

  2. 利用 libvirt 和 Linux 审计子系统跟踪 KVM 客户机

    在虚拟环境中审计和过滤客户机和主机事件 Marcelo H. Cerri, 软件工程师, IBM Marcelo Cerri 是位于巴西 IBM Linux Technology Center 的软件 ...

  3. Linux内核网络数据包发送(四)——Linux netdevice 子系统

    Linux内核网络数据包发送(四)--Linux netdevice 子系统 1. 前言 2. `dev_queue_xmit` and `__dev_queue_xmit` 2.1 `netdev_ ...

  4. Linux网络设备子系统

    Linux网络设备子系统 1. 前言 2. 网络设备子系统初始化 2.1 `struct softnet_data`变量初始化 2.2 SoftIRQ Handler 初始化 3. 网络数据监测和调优 ...

  5. Linux内核子系统

    分享Linux内核子系统结构图一张

  6. Linux时间子系统之(一):时间的基本概念【转】

    本文转载自:http://www.wowotech.net/timer_subsystem/time_concept.html 本文使用Q & A的方式来和大家以前探讨一下时间的基本概念 一. ...

  7. 【转】 linux iio子系统

    原文网址:http://blog.csdn.net/tsy20100200/article/details/47101661 最近由于工作的需要,接触了Linux iio子系统,对于这个目录其实以前是 ...

  8. 7.Linux 输入子系统分析

    为什么要引入输入子系统? 在前面我们写了一些简单的字符设备的驱动程序,我们是怎么样打开一个设备并操作的呢? 一般都是在执行应用程序时,open一个特定的设备文件,如:/dev/buttons 1 .. ...

  9. 【Linux 内核】Linux 操作系统结构 ( Linux 内核在操作系统中的层级 | Linux 内核子系统及关系 | 进程调度 | 内存管理 | 虚拟文件系统 | 网络管理 | 进程间通信 )

    文章目录 一.Linux 内核在操作系统中的层级 二.Linux 内核子系统 三.Linux 内核子系统之间的关系 一.Linux 内核在操作系统中的层级 Linux 内核 所在层级 : 整个计算机系 ...

  10. Linux时间子系统之三:时间的维护者:timekeeper

    专题文档汇总目录 Notes: 原文地址:Linux时间子系统之三:时间的维护者:timekeeper 本系列文章的前两节讨论了用于计时的时钟源:clocksource,以及内核内部时间的一些表示方法 ...

最新文章

  1. cocos2d-x温故(三)!
  2. linux6.6挂载u盘失败,mini2440 Linux系统自动挂载U盘与SD卡失败 解决方法
  3. java 读取中文配置文件问题
  4. Springboot整合swagger指南
  5. 中文手机评论情感分类系列(一)
  6. 前端学习(2927):今日总结
  7. delphi webbrowser 对象不支持_建模初学者,那些你可能还不知道的10个ZBrush小技巧!【值得收藏】...
  8. debian linux vnc,Debian 如何配置安装Xfce桌面+VNC远程桌面服务
  9. ICLR'17 | 在特征空间增强数据集
  10. 第九章:SpringCloud Feign几个坑
  11. 蛋疼! 注意了,千万不要在 MySQL 中使用 UTF-8
  12. Ubuntu下Hadoop的安装和配置
  13. strak组件(5):为列表定制预留钩子方法
  14. 用c语言电脑系统指令,c语言文件 DOS命令大全(10)
  15. 近期14个“AI产品经理”职位JD推荐(覆盖北京、上海、深圳、成都、重庆、杭州)
  16. Packet Tracer搭建局域网以及实现局域网互通
  17. 乒乓球十一分制比赛规则_乒乓球比赛规则
  18. 转:马明哲:拥有执行力才能让你强大
  19. 新闻App详细开发流程和结构搭建
  20. cf显示服务器登录,cf显示与服务器

热门文章

  1. 安装Ubuntu Core系统
  2. 黄河金岸诗词赋联大赛获奖名单
  3. 计算机用户文件夹加密,电脑怎么设置加密文件夹_给电脑文件夹设置密码的方法...
  4. 元宇宙虚拟人迎来高峰期,哪个是你的最爱?
  5. 幅频特性曲线protues_频率响应,幅频特性,增益与相位裕量,波特图
  6. Fedora14 基于Qt的UDP传输文字聊天小软件实现 (Qt查询本地Ip、Qt本地时间显示、传输中文汉字实现、Qt的textedit自动滚屏实现、给QPushButton设键盘快捷实现)---续上
  7. Intent简单介绍
  8. 什么是计算机?计算机的硬件系统组成有哪些?
  9. 激光雷达3D建模---读书笔记
  10. 数据分析-数据清洗与整理