4.平台设备+class内创建sysfs的节点+简单的dts调用

Posted 我爱一次性

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了4.平台设备+class内创建sysfs的节点+简单的dts调用相关的知识,希望对你有一定的参考价值。

前言:其实通常驱动都只是修修改改,很少需要重零开始一个个字母敲的。但是,我总觉得不重新敲,心里对学习驱动框架很不踏实。像是平台设备,有多少新手不懂为啥写成这样呢。可能吧,我比较菜。以前平台设备,设备树,sysfs经常改,但都是我心里不清楚逻辑的。经过一段时间的研究终于大概清楚了。

目录

1.平台设备

2.例子(用设备树的)

接下来,我开始分解:

2.1 class的创建字段

效果图:

2.2 DTS获取部分


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调用

给驱动添加sysfs设备模型(基于中断的按键程序)

RK3399平台开发系列讲解(内核调试篇)9.17添加Sysfs节点(kobject和kset)

字符设备驱动平台设备驱动设备驱动模型sysfs的关系

[kernel]字符设备驱动平台设备驱动设备驱动模型sysfs几者之间的比较和关联

字符设备驱动平台设备驱动设备驱动模型sysfs的比较和关联