【LED子系统】七、触发器实现

img
个人主页:董哥聊技术
我是董哥,高级嵌入式软件开发工程师,从事嵌入式Linux驱动开发和系统开发,曾就职于世界500强公司!
创作理念:专注分享高质量嵌入式文章,让大家读有所得!
img

文章目录

    • 1、前言
    • 2、触发器
    • 3、heartbeat触发器的注册注销
    • 4、heartbeat触发器相关定义和实现
      • 4.1 heartbeat_panic_nb
      • 4.2 heartbeat_reboot_nb
      • 4.3 heartbeat_led_trigger
      • 4.4 heartbeat_trig_groups实现
      • 4.5 struct heartbeat_trig_data
      • 4.6 heartbeat_trig_activate
      • 4.7 heartbeat_trig_deactivate
      • 4.8 led_heartbeat_function
    • 5、总结

1、前言

上面几篇文章,我们详细解释了核心层的相关实现,主要包括:led-core.cled-class.cled-triggers.c

下面我们来主要了解一下LED触发器的实现

image-20230417084033734

在上文中,我们提及到led-triggers.c中,只是对外提供了注册注销接口,闪烁设置接口,那么要想去实现闪烁功能,必定要有一个地方去调用这些函数!

 

2、触发器

我们在第一章已经了解到在drivers/leds/trigger/ledtrig-xxx.c中,都是关于触发器的实现,常见的触 发方式如下表所示:

触发方式说明
none无触发方式
disk-activity硬盘活动
nand-disknand flash活动
mtdmtd设备活动
timer定时器
heartbeat系统心跳

不同的触发方式,以控制LED实现不同的效果!下面我们以ledtrig-heartbeat.c为例分析,打开该文件!

 

3、heartbeat触发器的注册注销

static int __init heartbeat_trig_init(void)
{
    int rc = led_trigger_register(&heartbeat_led_trigger);		//	注册到LED子系统框架中

    if (!rc) {
        atomic_notifier_chain_register(&panic_notifier_list,
                           &heartbeat_panic_nb);				//	注册panic通知链路
        register_reboot_notifier(&heartbeat_reboot_nb);			//	注册到重启通知链路
    }
    return rc;
}

static void __exit heartbeat_trig_exit(void)
{
    unregister_reboot_notifier(&heartbeat_reboot_nb);
    atomic_notifier_chain_unregister(&panic_notifier_list,
                     &heartbeat_panic_nb);
    led_trigger_unregister(&heartbeat_led_trigger);
}

module_init(heartbeat_trig_init);
module_exit(heartbeat_trig_exit);

MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
MODULE_DESCRIPTION("Heartbeat LED trigger");
MODULE_LICENSE("GPL v2");

我们分析驱动,一般都以module_init为入口函数,

函数介绍module_init该函数主要将heartbeat_led_trigger定义的触发器注册进入led_classdev中,说明白点,也就是LED子系统中。

实现思路

  1. 调用led_trigger_register接口,将定义的触发器,添加到LED子系统中
  2. 调用atomic_notifier_chain_register接口,将heartbeat_panic_nb注册到panic_notifier_list链路中,在内核发生panic时将调用该链。
  3. 调用register_reboot_notifier接口,注册heartbeat_reboot_nb通知函数,在系统重新启动时将调用该notifier

 

4、heartbeat触发器相关定义和实现

heartbeat_trig_init函数中,我们看到了heartbeat_led_trigger变量,heartbeat_panic_nbheartbeat_reboot_nb等一系列未知函数,那么下面我们分析这些变量的定义和实现!

4.1 heartbeat_panic_nb

static struct notifier_block heartbeat_panic_nb = {
	.notifier_call = heartbeat_panic_notifier,
};

static int heartbeat_panic_notifier(struct notifier_block *nb,
				     unsigned long code, void *unused)
{
	panic_heartbeats = 1;
	return NOTIFY_DONE;
}

函数介绍:该函数主要用于kernel出发panic后执行的函数,这里设置panic_heartbeats = 1后,关闭LED

 

4.2 heartbeat_reboot_nb

static struct notifier_block heartbeat_reboot_nb = {
	.notifier_call = heartbeat_reboot_notifier,
};

static int heartbeat_reboot_notifier(struct notifier_block *nb,
				     unsigned long code, void *unused)
{
	led_trigger_unregister(&heartbeat_led_trigger);
	return NOTIFY_DONE;
}

函数介绍:该函数主要用于系统重启时,调用该函数,来注销掉此触发器。

 

4.3 heartbeat_led_trigger

static struct led_trigger heartbeat_led_trigger = {
	.name     = "heartbeat",
	.activate = heartbeat_trig_activate,
	.deactivate = heartbeat_trig_deactivate,
	.groups = heartbeat_trig_groups,
};

led_trigger结构体在之前文章有分析过, 这里创建heartbeat_led_trigger心跳触发器,然后并配置相关激活函数和sysfs对应的设备文件。

 

4.4 heartbeat_trig_groups实现

static ssize_t led_invert_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct heartbeat_trig_data *heartbeat_data =
		led_trigger_get_drvdata(dev);

	return sprintf(buf, "%u\n", heartbeat_data->invert);
}

static ssize_t led_invert_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	struct heartbeat_trig_data *heartbeat_data =
		led_trigger_get_drvdata(dev);
	unsigned long state;
	int ret;

	ret = kstrtoul(buf, 0, &state);
	if (ret)
		return ret;

	heartbeat_data->invert = !!state;

	return size;
}

static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);

static struct attribute *heartbeat_trig_attrs[] = {
	&dev_attr_invert.attr,
	NULL
};
ATTRIBUTE_GROUPS(heartbeat_trig);

函数介绍led_invert_showled_invert_storesysfs文件系统中,对外开放的文件节点,这两个函数实现了文件节点的读写。

实现思路

  1. 调用led_trigger_get_drvdata接口,获取heartbeat_trig_data相关数据
  2. 读写heartbeat_datainvert属性

这里牵涉到heartbeat_trig_data结构体,我们来了解下

 

4.5 struct heartbeat_trig_data

往下分析前,我们先看一下这个结构体

struct heartbeat_trig_data {
	struct led_classdev *led_cdev;
	unsigned int phase;
	unsigned int period;
	struct timer_list timer;
	unsigned int invert;
};
  • led_cdev:指向struct led_classdev对象的指针
  • phase:表示心跳当前阶段
  • period:表示心跳周期
  • timer:用于调度心跳函数的struct timer_list对象
  • invert:表示是否反转心跳。

 

4.6 heartbeat_trig_activate

static int heartbeat_trig_activate(struct led_classdev *led_cdev)
{
	struct heartbeat_trig_data *heartbeat_data;

	heartbeat_data = kzalloc(sizeof(*heartbeat_data), GFP_KERNEL);
	if (!heartbeat_data)
		return -ENOMEM;

	led_set_trigger_data(led_cdev, heartbeat_data);
	heartbeat_data->led_cdev = led_cdev;

	timer_setup(&heartbeat_data->timer, led_heartbeat_function, 0);
	heartbeat_data->phase = 0;
	if (!led_cdev->blink_brightness)
		led_cdev->blink_brightness = led_cdev->max_brightness;
	led_heartbeat_function(&heartbeat_data->timer);
	set_bit(LED_BLINK_SW, &led_cdev->work_flags);

	return 0;
}

函数介绍:该函数的主要功能是为激活对应的LED触发器。

实现方式

  1. 定义heartbeat_trig_data结构体变量,表示触发器的相关数据
  2. 调用led_set_trigger_data接口,将结构体heartbeat_trig_dataled_classdev关联起来
  3. 调用timer_setup接口,配置触发器的定时器函数
  4. 最后调用set_bit,设置工作位为LED_BLINK_SW,表示触发器正常工作

 

4.7 heartbeat_trig_deactivate

static void heartbeat_trig_deactivate(struct led_classdev *led_cdev)
{
	struct heartbeat_trig_data *heartbeat_data =
		led_get_trigger_data(led_cdev);

	del_timer_sync(&heartbeat_data->timer);
	kfree(heartbeat_data);
	clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
}

函数介绍:该函数的主要功能是将对应的LED触发器失效

实现方式

  1. 调用del_timer_sync接口,删除定时器
  2. 调用clear_bit接口,清除LED_BLINK_SW位,表示定时器关闭

 

4.8 led_heartbeat_function

static void led_heartbeat_function(struct timer_list *t)
{
    struct heartbeat_trig_data *heartbeat_data =
        from_timer(heartbeat_data, t, timer);			//	获取heartbeat_trig_data数据结构指针
    struct led_classdev *led_cdev;
    unsigned long brightness = LED_OFF;
    unsigned long delay = 0;

    led_cdev = heartbeat_data->led_cdev;

    if (unlikely(panic_heartbeats)) {
        led_set_brightness_nosleep(led_cdev, LED_OFF);
        return;
    }

    if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE, &led_cdev->work_flags))
        led_cdev->blink_brightness = led_cdev->new_blink_brightness;				//	判断是否亮度有变化	

    /* acts like an actual heart beat -- ie thump-thump-pause... */
    switch (heartbeat_data->phase) {
    case 0:
        /*
         * The hyperbolic function below modifies the
         * heartbeat period length in dependency of the
         * current (1min) load. It goes through the points
         * f(0)=1260, f(1)=860, f(5)=510, f(inf)->300.
         */
        heartbeat_data->period = 300 +
            (6720 << FSHIFT) / (5 * avenrun[0] + (7 << FSHIFT));
        heartbeat_data->period =
            msecs_to_jiffies(heartbeat_data->period);
        delay = msecs_to_jiffies(70);
        heartbeat_data->phase++;
        if (!heartbeat_data->invert)
            brightness = led_cdev->blink_brightness;
        break;
    case 1:
        delay = heartbeat_data->period / 4 - msecs_to_jiffies(70);
        heartbeat_data->phase++;
        if (heartbeat_data->invert)
            brightness = led_cdev->blink_brightness;
        break;
    case 2:
        delay = msecs_to_jiffies(70);
        heartbeat_data->phase++;
        if (!heartbeat_data->invert)
            brightness = led_cdev->blink_brightness;
        break;
    default:
        delay = heartbeat_data->period - heartbeat_data->period / 4 -
            msecs_to_jiffies(70);
        heartbeat_data->phase = 0;
        if (heartbeat_data->invert)
            brightness = led_cdev->blink_brightness;
        break;
    }

    led_set_brightness_nosleep(led_cdev, brightness);
    mod_timer(&heartbeat_data->timer, jiffies + delay);
}

函数介绍led_heartbeat_function 是定时器的核心函数,也是控制心跳LED触发器的关键函数。

实现思路

  1. 通过调用from_timer来获取结构体heartbeat_trig_activate的首地址空间
  2. 调用test_and_clear_bit,判断是否亮度发生变化,如果变化,赋值新的亮度
  3. 中间的case主要适配CPU不同的负载来稳定产生一个稳定的定时效果,并且根据heartbeat_data->invert的值,设置亮灭效果。(PS:注意这里判断heartbeat_data->invert的时候,是有无!的区别来实现亮灭的)
  4. 最后调用led_set_brightness_nosleep将闪烁亮度和延迟设置进去。

 

5、总结

该部分主要实现了心跳灯的效果,表示Linux正常运行,正常情况下,我们这里只需要将该驱动编译进去内核即可实现该效果!

这个驱动程序主要有两个步骤:

  1. 实现驱动框架,赋值相关函数
  2. 产生一个定时器,循环亮灭

 

好啦,到这里我们基本将LED子系统全部详细了解完毕了,真的可谓麻雀虽小,五脏俱全呀,仅仅一个LED子系统,总结下来也花费不少时间,同时也希望大家多多点赞,收藏,支持一波!

点赞+关注,永远不迷路

img
欢迎关注【嵌入式艺术】,董哥原创!

http://www.niftyadmin.cn/n/350977.html

相关文章

SolVES模型生态系统服务功能社会价值评估

查看原文>>>SolVES 模型生态系统服务功能社会价值评估&#xff08;基于多源环境QGIS、PostgreSQL、ArcGIS、Maxent、R语言&#xff09; 目录 第一章、理论基础与研究热点 第二章、SolVES 4.0 模型运行环境配置 第三章、SolVES 4.0 模型运行 第四章、数据获取与入…

如何对Windows剪切板里的内容进行取证分析 Windows剪切板取证

前言 无论是在现实中对设备进行取证分析&#xff0c;还是在ctf中做取证类的题目&#xff0c;剪切板里的内容都需要去查看&#xff0c;以免遗漏什么重要信息 剪切板位置 剪切板是计算机操作系统提供的一个临时存储区域&#xff0c;用于在不同应用程序之间复制和粘贴文本、图像…

chatgpt赋能Python-python_ip归属地

Python IP归属地查询 在网络安全领域&#xff0c;IP地址归属地查询是一项非常重要的任务。很多时候我们需要知道某个IP地址的归属地以解决一些安全问题。Python语言在这方面也发挥了巨大的作用&#xff0c;有各种成熟的IP地址归属地查询库。在本文中&#xff0c;我们将介绍如何…

百分位数、数据分布、直方图、正态数据分布

目录 1、百分位数 2、数据分布 3、正态数据分布 1、百分位数 统计学中使用百分位数&#xff08;Percentiles&#xff09;提供一个数字&#xff0c;该数字描述了给定百分比值小于的值。 例如&#xff1a;假设我们有一个数组&#xff0c;包含一时刻一条街上人的年龄 arr […

【追梦之旅】— 堆的实际应用--TopK问题

【追梦之旅】— 堆的实际应用--TopK问题&#x1f60e; 前言&#x1f64c;堆的TopK问题的现实栗子堆的TopK思路的应用场景堆的TopK思路的具体实现fscanf函数fprintf函数堆的TopK具体实现代码&#xff1a;前K个数据的巧妙设置运行结果截图&#xff1a; 总结撒花&#x1f49e; &am…

企业即时通讯如何让企业沟通变得简单

企业即时通讯&#xff0c;企业之间的沟通协作&#xff0c;最核心的价值在于能够将复杂的工作任务简化为更高效、更易于沟通的协作方式。如果员工之间没有协作&#xff0c;就没有办法进行高效的沟通&#xff0c;就会出现组织低效、沟通效率低等问题。那么如何将复杂的工作任务简…

云原生之深入解析Kubernetes如何使用Leader选举机制来实现自己的HA应用

一、背景 在 Kubernetes 的 kube-controller-manager、kube-scheduler&#xff0c;以及使用 Operator 的底层实现 controller-rumtime 都支持高可用系统中的 leader 选举&#xff0c;那么 controller-rumtime&#xff08;底层的实现是 client-go&#xff09; 中的 leader 选举…

智能结构诊断器:建筑结构健康的守护者

近年来&#xff0c;接二连三的自建房坍塌&#xff0c;超高层建筑震动&#xff0c;让建筑的健康和安全性成为了人们关注的焦点。为了确保建筑物的长期稳定性和安全性&#xff0c;迫切需要高效且准确的方法来监测结构的健康状况。智能结构诊断器的出现&#xff0c;让建筑结构监测…