4.平台设备+class内创建sysfs的节点+简单的dts调用
Posted 我爱一次性
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了4.平台设备+class内创建sysfs的节点+简单的dts调用相关的知识,希望对你有一定的参考价值。
前言:其实通常驱动都只是修修改改,很少需要重零开始一个个字母敲的。但是,我总觉得不重新敲,心里对学习驱动框架很不踏实。像是平台设备,有多少新手不懂为啥写成这样呢。可能吧,我比较菜。以前平台设备,设备树,sysfs经常改,但都是我心里不清楚逻辑的。经过一段时间的研究终于大概清楚了。
目录
1.平台设备
平台设备就和其他总线一样,比如i2c总线。只是平台设备的总线是 人为注册 的。作用就是分开 设备 和 驱动 。体现一个 机制 与 策略 分离。
组成:
虚拟的总线platform_bus_type:这个我们一般不用管,用kernel的 bus 框架创造的。
设备device: 用了给设备的描述信息。比如引脚口,触发方式等等。但是随着设备牌子种类越来越多,那么就有很多if语句去判断设备信息。这个东西很多时候被dts取代了。在后面内核会发现有 驱动 无对应匹配的 驱动 情况。那么就是把设备的描述信息在设备树写着。
驱动driver:在设备或者设备树给了描述信息后,写驱动给设备的机制。
ps1:平台设备是个框架,这个三个组成部分一者占一个c文件。总线我们不用管。我们填充好 驱动 和 设备 就行了。在有设备树的情况下,填充 驱动 与 dts 。
ps2:写了框架不代表 传统意义上的驱动创建好。比如sysfs的class,/dev/这些东西还得在driver文件里面写好。应该继续写 字符设备 或者 复杂设备 ,创建class节点。
2.例子(用设备树的)
/*
kernel 5.1
*/
#include<linux/platform_device.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/sysfs.h>
static int class_test = 0;
static ssize_t class_test_show(struct class *class, struct class_attribute *attr, char *buf)
return (sprintf(buf, "%u\\n", class_test));
static ssize_t class_test_store(struct class *class, struct class_attribute *attr, const char *buf, size_t len)
int rc;
rc = kstrtouint(buf, 0, &class_test);
if (rc < 0)
pr_err("get class_test value failed!\\n");
return len;
static struct class_attribute class_test_add =
__ATTR(class_test, 0664, class_test_show, class_test_store);
static struct attribute *Odin_fan_class_attrs[] =
&class_test_add.attr,
NULL,
;
ATTRIBUTE_GROUPS(Odin_fan_class);
//先创造 Odin_fan_class_groups ,里面再包含 Odin_fan_class_attrs
static struct class Odin_fan_class =
.name = "Odin_pi_fan",
.owner = THIS_MODULE,
.class_groups = Odin_fan_class_groups,
/*.class_attrs = Odin_fan_class_attrs,
4.9内核用这个 前面类属性注册也不一样,例子用的5.1
旧版本的内核参照同同版本驱动的例子修改,大同小异
*/
;
int Odin_fan_probe(struct platform_device *pdev)
int adb = 0;
int ret;
ret = device_property_read_u32(&pdev->dev,"adb",&adb);
printk("<3>""oncethings adb:%d ret:%d",adb,ret);
return 0;
int Odin_fan_remove(struct platform_device *pdev)
return 0;
static const struct of_device_id Odin_fan_of_match[] =
.compatible = "Odin-fan-gpio" ,
/* sentinel */,
;
static struct platform_driver Odin_fan_driver =
.driver =
.name = "Odin_pi_fan", // /sys/bus/platform/drivers
.of_match_table = Odin_fan_of_match, //匹配设备树
,
.probe = Odin_fan_probe,
.remove = Odin_fan_remove,
;
static int __init Odin_fan_driver_init(void)
int rc = 0;
rc = class_register(&Odin_fan_class);
if(rc == 0)
rc = platform_driver_register(&Odin_fan_driver);
return rc;
static void __exit Odin_fan_driver_exit(void)
platform_driver_unregister(&Odin_fan_driver);
class_unregister(&Odin_fan_class);
module_init(Odin_fan_driver_init);
module_exit(Odin_fan_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("oncethings");
接下来,我开始分解:
2.1 class的创建字段
static int class_test = 0;
static ssize_t class_test_show(struct class *class, struct class_attribute *attr, char *buf)
return (sprintf(buf, "%u\\n", class_test));
static ssize_t class_test_store(struct class *class, struct class_attribute *attr, const char *buf, size_t len)
int rc;
rc = kstrtouint(buf, 0, &class_test);
if (rc < 0)
pr_err("get class_test value failed!\\n");
return len;
static struct class_attribute class_test_add =
__ATTR(class_test, 0664, class_test_show, class_test_store);
static struct attribute *Odin_fan_class_attrs[] =
&class_test_add.attr,
NULL,
;
ATTRIBUTE_GROUPS(Odin_fan_class);
//先创造 Odin_fan_class_groups ,里面再包含 Odin_fan_class_attrs
static struct class Odin_fan_class =
.name = "Odin_pi_fan",// /sys/class/Odin_pi_fan/ 的名字
.owner = THIS_MODULE,
.class_groups = Odin_fan_class_groups,
/*.class_attrs = Odin_fan_class_attrs,
4.9内核用这个 前面类属性注册也不一样,例子用的5.1
旧版本的内核参照同同版本驱动的例子修改,大同小异
*/
;
static int __init Odin_fan_driver_init(void)
int rc = 0;
rc = class_register(&Odin_fan_class);
.....
return rc;
ps1:写完后的作用就是在 /sys/class/Odin_pi_fan/class_test有个节点 这个节点我们可以cat echo 操作该节点。
ps2:ATTRIBUTE_GROUPS(Odin_fan_class); 这个是5.1有点,对于不同版本的class注册请参照同版本的其他驱动注册。本人4.9版本就和这个有点不同。
ps3:__ATTR(class_test, 0664, class_test_show, class_test_store);
664貌似已经是最高权限了,本人试过在安卓10的kernel写666都是编译失败的。
ps4:class_register(&Odin_fan_class); 用于注册class,与之匹配的是 class_unregister(&Odin_fan_class);
另外 class_create() 和 class_destroy() 匹配。 前者是写了把已有的class注册,后者class_create则是填写一个名字,并按照该名字创造一个class后返回该class。随便用一个就行。
同样,用了class_create()得到的类可以直接赋值groups的,即class的节点,ex:
//kernel 5.1
static ssize_t bl_power_show(struct device *dev, struct device_attribute *attr,
char *buf)
............
static ssize_t bl_power_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
............
static DEVICE_ATTR_RW(bl_power);//5.1连函数读写,起名都帮你弄了前缀,没有自由
static struct attribute *bl_device_attrs[] =
&dev_attr_bl_power.attr,
....
NULL,
;
...
ATTRIBUTE_GROUPS(bl_device);//同样帮你把名字+ _group 后缀
...
static int __init backlight_class_init(void)
...
backlight_class = class_create(THIS_MODULE, "backlight");
backlight_class->dev_groups = bl_device_groups;
....
效果图:
2.2 DTS获取部分
/arch/arm/boot/dts/xxx.dts or dtsi
fan@00000000
compatible = "Odin-fan-gpio";
adb = <123>;
;
fan:名字
@00000000:正常来说是填地址,我这里只是个测试,也没地址
compatible :兼容字段,驱动凭着后面字段找该树节点。
我们的目标就是获取 adb 后面的 123 数值。
int Odin_fan_probe(struct platform_device *pdev)
int adb = 0;
int ret;
ret = device_property_read_u32(&pdev->dev,"adb",&adb);
//寻找adb字段
printk("<3>""oncethings adb:%d ret:%d",adb,ret);
return 0;
static const struct of_device_id Odin_fan_of_match[] =
.compatible = "Odin-fan-gpio" , //该名字与dts中的名字相同
/* sentinel */,
;
static struct platform_driver Odin_fan_driver =
.driver =
.name = "Odin_pi_fan",
.of_match_table = Odin_fan_of_match, //匹配设备树
,
.probe = Odin_fan_probe,
.remove = Odin_fan_remove,
;
ps1:dts在编译后生成dtb,kernel启动时,会加载dtb这块内存,并获取设备信息。然后在平台设备注册时,就已经把该树节点 Odin_pi_fan 里的所有节点信息都有了。所以在回调函数 probe 里再过滤拿到adb里的信息。
无设备树版迟点写
以上是关于4.平台设备+class内创建sysfs的节点+简单的dts调用的主要内容,如果未能解决你的问题,请参考以下文章
4.平台设备+class内创建sysfs的节点+简单的dts调用
RK3399平台开发系列讲解(内核调试篇)9.17添加Sysfs节点(kobject和kset)