devm_of_led_classdev_register 函数

news/2024/11/14 15:37:30/

Linux version: 4.14

Code link: Linux source code (v4.14) - Bootlin


 1  devm_of_led_classdev_register 函数

int devm_of_led_classdev_register(struct device *parent,struct device_node *np,struct led_classdev *led_cdev)
{struct led_classdev **dr;int rc;dr = devres_alloc(devm_led_classdev_release, sizeof(*dr), GFP_KERNEL);if (!dr)return -ENOMEM;rc = of_led_classdev_register(parent, np, led_cdev);if (rc) {devres_free(dr);return rc;}*dr = led_cdev;devres_add(parent, dr);return 0;
}
EXPORT_SYMBOL_GPL(devm_of_led_classdev_register);

2 of_led_classdev_register 函数

int of_led_classdev_register(struct device *parent, struct device_node *np,struct led_classdev *led_cdev)
{char name[LED_MAX_NAME_SIZE];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);if (IS_ERR(led_cdev->dev))return PTR_ERR(led_cdev->dev);led_cdev->dev->of_node = np;if (ret)dev_warn(parent, "Led %s renamed to %s due to name collision",led_cdev->name, dev_name(led_cdev->dev));if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {ret = led_add_brightness_hw_changed(led_cdev);if (ret) {device_unregister(led_cdev->dev);return ret;}}led_cdev->work_flags = 0;
#ifdef CONFIG_LEDS_TRIGGERSinit_rwsem(&led_cdev->trigger_lock);
#endif
#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGEDled_cdev->brightness_hw_changed = -1;
#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);up_write(&leds_list_lock);if (!led_cdev->max_brightness)led_cdev->max_brightness = LED_FULL;led_update_brightness(led_cdev);led_init_core(led_cdev);#ifdef CONFIG_LEDS_TRIGGERSled_trigger_set_default(led_cdev);
#endifdev_dbg(parent, "Registered led device: %s\n",led_cdev->name);return 0;
}
EXPORT_SYMBOL_GPL(of_led_classdev_register);

(1) led_classdev_next_name

该函数决定 LED 设备在文件系统里面的名称。从设备树节点里面获取到的名称(label属性)作为初始的 name,然后遍历全局类 leds_class(class类型),跟里面的设备逐个对比,如果已经有同名的设备了,则在 name 后面添加 _x 形式的后缀,然后再次逐个对比,直到 leds_class 里面找不到同名的设备了,则表示该名称可以用于创建新设备了。该函数返回的 ret 为检测到命名冲突的次数。

static int led_classdev_next_name(const char *init_name, char *name,size_t len)
{unsigned int i = 0;int ret = 0;struct device *dev;strlcpy(name, init_name, len);while ((ret < len) &&(dev = class_find_device(leds_class, NULL, name, match_name))) {put_device(dev);ret = snprintf(name, len, "%s_%u", init_name, ++i);}if (ret >= len)return -ENOMEM;return i;
}

①  class_find_device 函数

该函数遍历 class 中的 device ,返回符合 match 条件的 device 结构体变量。

struct device *class_find_device(struct class *class, struct device *start,const void *data,int (*match)(struct device *, const void *))
{struct class_dev_iter iter;struct device *dev;if (!class)return NULL;if (!class->p) {WARN(1, "%s called for class '%s' before it was initialized",__func__, class->name);return NULL;}class_dev_iter_init(&iter, class, start, NULL);while ((dev = class_dev_iter_next(&iter))) {if (match(dev, data)) {get_device(dev);break;}}class_dev_iter_exit(&iter);return dev;
}
EXPORT_SYMBOL_GPL(class_find_device);

② class_dev_iter_next 函数

该函数根据输入的 iter 类型的变量返回下一个 device 变量

struct device *class_dev_iter_next(struct class_dev_iter *iter)
{struct klist_node *knode;struct device *dev;while (1) {knode = klist_next(&iter->ki);if (!knode)return NULL;dev = container_of(knode, struct device, knode_class);if (!iter->type || iter->type == dev->type)return dev;}
}
EXPORT_SYMBOL_GPL(class_dev_iter_next);

(2)device_create_with_groups

该函数在全局类led_class下创建一个LED设备,这里参数parent为LED分组,所以在文件系统里面,该LED设备的节点会被创建在LED分组下面,而不是通常的/dev目录下面。

struct device *device_create_with_groups(struct class *class,struct device *parent, dev_t devt,void *drvdata,const struct attribute_group **groups,const char *fmt, ...)
{va_list vargs;struct device *dev;va_start(vargs, fmt);dev = device_create_groups_vargs(class, parent, devt, drvdata, groups,fmt, vargs);va_end(vargs);return dev;
}
EXPORT_SYMBOL_GPL(device_create_with_groups);

 ① device_create_groups_vargs 函数

static struct device *
device_create_groups_vargs(struct class *class, struct device *parent,dev_t devt, void *drvdata,const struct attribute_group **groups,const char *fmt, va_list args)
{struct device *dev = NULL;int retval = -ENODEV;if (class == NULL || IS_ERR(class))goto error;dev = kzalloc(sizeof(*dev), GFP_KERNEL);if (!dev) {retval = -ENOMEM;goto error;}device_initialize(dev);dev->devt = devt;dev->class = class;dev->parent = parent;dev->groups = groups;dev->release = device_create_release;dev_set_drvdata(dev, drvdata);retval = kobject_set_name_vargs(&dev->kobj, fmt, args);if (retval)goto error;retval = device_add(dev);if (retval)goto error;return dev;error:put_device(dev);return ERR_PTR(retval);
}

② kobject_set_name_vargs

该函数设置kobject的名称

int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,va_list vargs)
{const char *s;if (kobj->name && !fmt)return 0;s = kvasprintf_const(GFP_KERNEL, fmt, vargs);if (!s)return -ENOMEM;/** ewww... some of these buggers have '/' in the name ... If* that's the case, we need to make sure we have an actual* allocated copy to modify, since kvasprintf_const may have* returned something from .rodata.*/if (strchr(s, '/')) {char *t;t = kstrdup(s, GFP_KERNEL);kfree_const(s);if (!t)return -ENOMEM;strreplace(t, '/', '!');s = t;}kfree_const(kobj->name);kobj->name = s;return 0;
}

③ device_add

 device_add就是将设备加入到Linux设备模型的关键,它的内部将找到它的bus,然后让它的bus给它找到它的driver,可参考:

八、device_add_device add_bit[7:4]_宁可一思进莫在一思停的博客-CSDN博客

(3)led_add_brightness_hw_changed

该函数创建brightness相关的文件 

static int led_add_brightness_hw_changed(struct led_classdev *led_cdev)
{struct device *dev = led_cdev->dev;int ret;ret = device_create_file(dev, &dev_attr_brightness_hw_changed);if (ret) {dev_err(dev, "Error creating brightness_hw_changed\n");return ret;}led_cdev->brightness_hw_changed_kn =sysfs_get_dirent(dev->kobj.sd, "brightness_hw_changed");if (!led_cdev->brightness_hw_changed_kn) {dev_err(dev, "Error getting brightness_hw_changed kn\n");device_remove_file(dev, &dev_attr_brightness_hw_changed);return -ENXIO;}return 0;
}

(4)led_update_brightness

该函数更新 led 的亮度状态 

int led_update_brightness(struct led_classdev *led_cdev)
{int ret = 0;if (led_cdev->brightness_get) {ret = led_cdev->brightness_get(led_cdev);if (ret >= 0) {led_cdev->brightness = ret;return 0;}}return ret;
}
EXPORT_SYMBOL_GPL(led_update_brightness);

(5)led_init_core

初始化工作队列和定时器,处理led的闪烁

void led_init_core(struct led_classdev *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);
}
EXPORT_SYMBOL_GPL(led_init_core);

(6)led_trigger_set_default

该函数设置led的默认触发状态。 

void led_trigger_set_default(struct led_classdev *led_cdev)
{struct led_trigger *trig;if (!led_cdev->default_trigger)return;down_read(&triggers_list_lock);down_write(&led_cdev->trigger_lock);list_for_each_entry(trig, &trigger_list, next_trig) {if (!strcmp(led_cdev->default_trigger, trig->name))led_trigger_set(led_cdev, trig);}up_write(&led_cdev->trigger_lock);up_read(&triggers_list_lock);
}
EXPORT_SYMBOL_GPL(led_trigger_set_default);

3  devres_add 函数

在驱动代码中我们经常会见到一些以devm开头的函数,这一类的函数都是和设备资源管理相关的

devm架构中代表资源的机构体是struct devres和struct devres_node

devres_add就是注册函数,他把devres加入到device的相关链表中

void devres_add(struct device *dev, void *res)
{struct devres *dr = container_of(res, struct devres, data);unsigned long flags;spin_lock_irqsave(&dev->devres_lock, flags);add_dr(dev, &dr->node);spin_unlock_irqrestore(&dev->devres_lock, flags);
}
EXPORT_SYMBOL_GPL(devres_add);

补充:

从 uboot 到 kernel 再到 /sys/class,然后注册 leds 类,再实例化一个 LED 灯。

/* uboot */
boot_jump_linux()announce_and_cleanup()printf("\nStarting kernel ...%s\n\n"); // printf() bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");cleanup_before_linux()kernel_entry(0, machid, r2);/* kernel */
start_kernel()rest_init() // Do the rest non-__init'ed, we're now alivekernel_thread(kernel_init, NULL, CLONE_FS);kernel_init()kernel_init_freeable()/** Ok, the machine is now initialized. None of the devices* have been touched yet, but the CPU subsystem is up and* running, and memory and process management works.** Now we can finally start doing some real work..*/do_basic_setup()driver_init() // to initialize their subsystems.devtmpfs_init()devices_init()buses_init()classes_init()kset_create_and_add("class", NULL, NULL); // create a struct kset dynamically and add it to sysfskset_create()kobject_set_name()kset_register()kset_init()kobject_add_internal()kobject_get()kobj_kset_join()kset_get()list_add_tail()__list_add(){next->prev = new;new->next = next;new->prev = prev;}create_dir()firmware_init()hypervisor_init()platform_bus_init()cpu_dev_init()memory_dev_init()container_dev_init()of_core_init()subsys_initcall(leds_init);
leds_init()	// 创建 leds 类,即 /sys/class/leds 目录class_create()__class_create()__class_register()kset_register()led_classdev_register()of_led_classdev_register() // register a new object(对象) of led_classdev class.led_classdev_next_name()device_create_with_groups()led_add_brightness_hw_changed()list_add_tail() // add to the list of ledsled_update_brightness()//led_trigger_set_default()

参考:

一叶知秋,一个 LED 就能入门 Linux 内核_device_create_with_groups_Li-Yongjun的博客-CSDN博客


http://www.ppmy.cn/news/93227.html

相关文章

Windows本地快速搭建SFTP文件服务器,并端口映射实现公网远程访问

文章目录 1. 搭建SFTP服务器1.1 下载 freesshd服务器软件1.3 启动SFTP服务1.4 添加用户1.5 保存所有配置 2 安装SFTP客户端FileZilla测试2.1 配置一个本地SFTP站点2.2 内网连接测试成功 3 使用cpolar内网穿透3.1 创建SFTP隧道3.2 查看在线隧道列表 4. 使用SFTP客户端&#xff0…

解读智慧城市建设的关键角色:GIS技术的应用与优势

近年来&#xff0c;随着城市化进程的加快和信息技术的迅猛发展&#xff0c;智慧城市成为了城市发展的重要方向。而在智慧城市建设中&#xff0c;地理信息系统&#xff08;GIS&#xff09;技术的应用正发挥着越来越重要的作用。GIS技术以其独特的地理空间分析能力&#xff0c;为…

结对编程 --- 大部分程序员喜欢的编程方式

一、介绍 结对编程起源时间可以追溯到 1990 年代早期。这种编程方法最初由 Jim Highsmith 和 Alistair Cockburn 等人提出。后来&#xff0c;Kent Beck 和 Ward Cunningham 等人将其发展成为一种敏捷开发方法&#xff0c;被称为“极限编程”&#xff08;Extreme Programming&am…

WMS 概述 -- “窗口管理员“

WMS 概述 -- "窗口管理员" 1、WMS 职责2、涉及元素3、WMS、AMS与Activity间的关系 1、WMS 职责 WMS职责理解窗口管理WMS 是窗口的管理者&#xff0c;它负责窗口的启动、添加和删除&#xff0c;另外窗口的大小和层级也是由WMS 进行管理的。窗口管理的核心成员有 Disp…

Android 12.0Launcher3 去掉workspace长按弹出壁纸弹窗

1.概述 在12.0的系统开发中,在Launcher3开发中,在长按屏幕的时候,会弹出窗口,修改主屏幕配置,壁纸,等信息,由于要默认设置一些配置 不想让用户修改相关配置,这时候就需要去掉长按弹窗功能了,禁止修改相关配置 2.Launcher3 去掉workspace长按弹出壁纸弹窗的核心类 /p…

stable-diffusion-webui(1.2.1版本) win10系统本地电脑部署

在安装stable-diffusion-webui(1.2.1版本)之前需要确认win10本地电脑具有的环境 1.显卡类型&#xff1a;NVIDIA&#xff08;英伟达&#xff09; 建议显存高于4G以上&#xff0c;这样512*512的还是可以运行的 2.python(版本必须最好是3.10.6&#xff09;和 git最新版 正式…

【华为OD机试真题2023B卷 JAVA】TLV解码

华为OD2023(B卷)机试题库全覆盖,刷题指南点这里 TLV解码 知识点数组字符串 时间限制:1s 空间限制:256MB 限定语言:不限 题目描述: TLV编码是按[Tag Length Value]格式进行编码的,一段码流中的信元用Tag标识,Tag在码流中唯一不重复,Length表示信元Value的长度,Value…

历次工业革命的本质,都是能源转换的革命。(电学史的伟大瞬间)【电的本质】

文章目录 引言I 电学史的伟大瞬间1.1 电的本质1.2 电池的发明(电的性质)1.3 动手改造和利用电(电和磁的关系)1.4 电的普及和应用引言 历次工业革命的本质,都是能源转换的革命。 第一次工业革命:蒸汽机让人类走出了以人力和畜力为动力来源的时代 第二次工业革命:以电力为工…