创建sys下的属性节点. linux内核中提供有如下三个函数可以创建sys/class下的属性节点
1. device_create_file
2. driver_create_file
3. class_create_file
以上三个函数分别基于device/driver/class来创建属性控制节点,提供store和show函数接口供应用层调用
device_create_file 需要一个device作为参数,创建的属性节点在device设备节点对应的路径下,使用DEVICE_ATTR相关宏来生成属性操作函数
driver_create_file 需要一个driver作为参数,创建的属性节点在driver设备节点对应的路径下,使用DRIVER_ATTR_RW相关宏来生成属性操作函数
class_create_file 需要一个class作为参数,创建的属性节点在class设备节点对应的路径下,使用CLASS_ATTR_RW相关宏来生成属性操作函数
注意:
在最新的内核上(kernel-5.18) 有DEVICE_ATTR宏,没有 DRIVER_ATTR和CLASS_ATTR,只有DRIVER_ATTR_RW/RO/WO
和CLASS_ATTR_RW/RO/WO 这样的宏,内核将DRIVER_ATTR和CLASS_ATTR移除了,可能是基于安全角度出发
DEVICE_ATTR也有DEVICE_ATTR_RW/RO/WO. 猜测以后可能也会移除DEVICE_ATTR宏
具体可以参考
kernel/include/linux/device.h
kernel/include/linux/device/driver.h
kernel/include/linux/device/class.h
1. device_create_file
device_create_file会基于device的设备节点路径来创建属性节点,属性可以使用DEVICE_ATTR来创建
函数原型,定义在include/linux/device.h中
创建设备节点
int device_create_file(struct device *device,
const struct device_attribute *entry);
删除设备节点
void device_remove_file(struct device *dev,
const struct device_attribute *attr);
device_create_file需要一个struct device类型的参数,
该参数可以使用driver &device probe成功后传入的参数,
也可以使用device_create来手动创建,这两种情况下创建的设备节点的路径会有差异
参考实例:
device.c文件
/****************************
device 部分的代码 device.c
*****************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
static void demo_device_release(struct device *dev)
{
printk("%s,%d: Enter\n",__func__,__LINE__);
return;
}
static struct platform_device demo_device = {
.name = "demo",
.id = -1,
.dev.release = demo_device_release,
};
static int demo_device_init(void){
printk("%s,%d: Enter\n",__func__,__LINE__);
return platform_device_register(&demo_device);
}
static void demo_device_exit(void){
printk("%s,%d: Enter\n",__func__,__LINE__);
platform_device_unregister(&demo_device);
return;
}
MODULE_LICENSE("GPL");
module_init(demo_device_init);
module_exit(demo_device_exit);
driver.c 的示例代码
1.1 使用probe成功后传入的参数
device_create_file放到probe函数中
/**********************************
driver部分的代码 driver.c
***********************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/io.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <linux/of.h>
#include <linux/of_device.h>
#define VERIFY_OCTAL_PERMISSIONS //不定义这个无法设置为0666,只能设置为0644
static int demo_driver_probe(struct platform_device *pdev);
static int demo_driver_remove(struct platform_device *pdev);
static ssize_t control_show(struct device *dev,struct device_attribute *attr,
char *buf)
{
printk("%s,%d: Enter\n",__func__,__LINE__);
return 0;
}
static ssize_t control_store(struct device *dev,struct device_attribute *attr,
const char *buf, size_t count)
{
if(NULL == buf || count >255 || count == 0)
return -1;
printk("%s,%d: buf: %s count:%d\n",__func__,__LINE__,buf,count);
return count;
}
static DEVICE_ATTR(control,0666, control_show, control_store);
static struct platform_driver demo_driver ={
.probe = demo_driver_probe,
.driver = {
.name = "demo", //
},
.remove = demo_driver_remove,
};
static int demo_driver_probe(struct platform_device *pdev)
{
int ret;
printk("%s,%d: Enter\n",__func__,__LINE__);
ret = device_create_file(&pdev->dev, &dev_attr_control);
if (ret < 0){
printk("could not create sysfs files\n");
return ret;
}
return 0;
}
static int demo_driver_remove(struct platform_device *pdev)
{
printk("%s,%d: Enter\n",__func__,__LINE__);
device_remove_file(&pdev->dev, &dev_attr_control);
return 0;
}
static int demo_driver_init(void)
{
int ret;
printk("%s,%d: Enter\n",__func__,__LINE__);
return platform_driver_register(&demo_driver);
}
static void demo_driver_exit(void)
{
printk("%s,%d: Enter\n",__func__,__LINE__);
platform_driver_unregister(&demo_driver);
return;
}
MODULE_LICENSE("GPL");
module_init(demo_driver_init);
module_exit(demo_driver_exit);
Makefile文件
ifneq ($(KERNELRELEASE),)
obj-m:=device.o driver.o
else
KDIR :=/lib/modules/$(shell uname -r)/build
PWD :=$(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -f *.ko *.o *.mod.o *.symvers *.cmd *.mod.c *.order
endif
验证步骤
将device.c和driver.c Makefile文件放到一个文件目录下,执行make命令,会生成device.ko和driver.ko
按如下步骤安装ko
sudo insmod device.ko
sudo insmod driver.ko
属性节点生成在
/sys/bus/platform/devices/demo/control
使用
cat /sys/bus/platform/devices/demo/control,
可以看到control_show函数执行了

1.2 使用使用device_create来手动创建device
由于不需要使用probe成功后传入的参数,因此创建属性节点的操作可以放到demo_init中
/**********************************
driver部分的代码 driver.c
***********************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/io.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <linux/of.h>
#include <linux/of_device.h>
#define VERIFY_OCTAL_PERMISSIONS //不定义这个无法设置为0666,只能设置为0644
static int demo_driver_probe(struct platform_device *pdev);
static int demo_driver_remove(struct platform_device *pdev);
static ssize_t control_show(struct device *dev,struct device_attribute *attr,
char *buf)
{
printk("%s,%d: Enter\n",__func__,__LINE__);
return 0;
}
static ssize_t control_store(struct device *dev,struct device_attribute *attr,
const char *buf, size_t count)
{
if(NULL == buf || count >255 || count == 0)
return -1;
printk("%s,%d: buf: %s count:%d\n",__func__,__LINE__,buf,count);
return count;
}
static DEVICE_ATTR(control,0666, control_show, control_store);
static struct platform_driver demo_driver ={
.probe = demo_driver_probe,
.driver = {
.name = "demo", //
},
.remove = demo_driver_remove,
};
static int demo_driver_probe(struct platform_device *pdev){
int ret;
printk("%s,%d: Enter\n",__func__,__LINE__);
return 0;
}
static int demo_driver_remove(struct platform_device *pdev){
printk("%s,%d: Enter\n",__func__,__LINE__);
return 0;
}
static int demo_open(struct inode *inode, struct file *file){
printk("%s,%d: Enter",__func__,__LINE__);
return 0;
}
static ssize_t demo_read (struct file *file, char __user *userbuf, size_t count, loff_t *offset)
{
printk("%s,%d: Enter",__func__,__LINE__);
return 0;
}
static ssize_t demo_write (struct file *file, const char __user *userbuf, size_t count, loff_t *offset)
{
printk("%s,%d: Enter",__func__,__LINE__);
return 1;
}
static struct file_operations demo_ops =
{
.owner = THIS_MODULE,
.open = demo_open,
.read = demo_read,
.write = demo_write,
};
static int demo_major;
static struct class *demo_class;
struct device *demo_dev;
static int demo_driver_init(void)
{
int ret;
printk("%s,%d: Enter\n",__func__,__LINE__);
//1.创建一个字符设备
demo_major = register_chrdev(0,"demo", &demo_ops);//创建一个字符设备
//2.在sys/class/下创建一个demo_class的文件夹
demo_class = class_create(THIS_MODULE, "demo_class");
//3. 在sys/class/demo_class/创建一个设备节点demo
demo_dev= device_create(demo_class,NULL,MKDEV(demo_major,0),NULL,"demo");
//4. 创建sys/class/demo_class/demo/control的属性节点
ret = device_create_file(demo_dev, &dev_attr_control);
if (ret < 0){
printk("could not create sysfs files\n");
return ret;
}
return platform_driver_register(&demo_driver);
}
static void demo_driver_exit(void)
{
printk("%s,%d: Enter\n",__func__,__LINE__);
//1.移除sys/class/demo_class/demo/control的属性节点
device_remove_file(demo_dev, &dev_attr_control);
//2.移除在sys/class/demo_class/demo
device_destroy(demo_class,demo_dev->devt);//destory用device的设备号
//3.移除/sys/class/demo_class
class_destroy(demo_class);
platform_driver_unregister(&demo_driver);
return;
}
MODULE_LICENSE("GPL");
module_init(demo_driver_init);
module_exit(demo_driver_exit);
因为device是我们通过device_create创建的,因此属性节点路径位于
/sys/class/demo_class/demo/control
2. driver_create_file
driver_create_file于device_create_file很像,它是从driver的角度来创建属性节点,因此需要的参数为device_driver 类型。
函数原型为
extern int __must_check driver_create_file(struct device_driver *driver,
const struct driver_attribute *attr);
extern void driver_remove_file(struct device_driver *driver,
const struct driver_attribute *attr);
driver_create_file需要的属性使用DRIVER_ATTR_RW来生成
注. 没有DRIVER_ATTR,怀疑是新的linux内核中将其移除了。
DRIVER_ATTR_RW只需要传入属性节点的name即可,属性节点的默认权限是0644
如果是只读或者只写的,可以使用DRIVER_ATTR_RO或者DRIVER_ATTR_WO来创建属性参数
示例代码
/**********************************
driver部分的代码 driver.c
***********************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/io.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
static int demo_driver_probe(struct platform_device *pdev);
static int demo_driver_remove(struct platform_device *pdev);
static ssize_t control_show(struct device_driver *dev,char *buf)
{
printk("%s,%d: Enter\n",__func__,__LINE__);
return 0;
}
static ssize_t control_store(struct device_driver *driver,const char *buf, size_t count)
{
if(NULL == buf || count >255 || count == 0 )
return -1;
printk("buf:%s count:%d\n",buf,count);
return count;
}
static DRIVER_ATTR_RW(control);
static struct platform_driver demo_driver ={
.probe = demo_driver_probe,
.driver.name = "demo",
.remove = demo_driver_remove,
};
static int demo_driver_probe(struct platform_device *pdev){
int ret;
printk("%s,%d: Enter\n",__func__,__LINE__);
ret = driver_create_file(&demo_driver.driver, &driver_attr_control);
if(ret < 0) {
printk("could not create sysfs files\n");
return ret;
}
return 0;
}
static int demo_driver_remove(struct platform_device *pdev){
printk("%s,%d: Enter\n",__func__,__LINE__);
return 0;
}
static int demo_driver_init(void)
{
int ret;
printk("%s,%d: Enter\n",__func__,__LINE__);
return platform_driver_register(&demo_driver);
}
static void demo_driver_exit(void)
{
printk("%s,%d: Enter\n",__func__,__LINE__);
driver_remove_file(&demo_driver.driver, &driver_attr_control);
platform_driver_unregister(&demo_driver);
return;
}
MODULE_LICENSE("GPL");
module_init(demo_driver_init);
module_exit(demo_driver_exit);
属性节点生成在
/sys/bus/platform/drivers/demo/control
3. class_create_file
class_create_file是基于类来创建属性节点,不依赖于device或driver,使用CLASS_ATTR_RW来创建
/**********************************
driver部分的代码 driver.c
***********************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/io.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
static int demo_driver_probe(struct platform_device *pdev);
static int demo_driver_remove(struct platform_device *pdev);
static ssize_t control_show(struct class *class, struct class_attribute *attr,
char *buf)
{
printk("%s,%d: Enter\n",__func__,__LINE__);
return 0;
}
static ssize_t control_store(struct class *class, struct class_attribute *attr,
const char *buf, size_t count)
{
if(NULL == buf || count >255 || count == 0 || strnchr(buf, count, 0x20))
return -1;
printk("buf:%s count:%d\n",buf,count);
return count;
}
static CLASS_ATTR_RW(control);
static struct platform_driver demo_driver ={
.probe = demo_driver_probe,
.driver.name = "demo",
.remove = demo_driver_remove,
};
static int demo_driver_probe(struct platform_device *pdev)
{
int ret;
printk("%s,%d: Enter\n",__func__,__LINE__);
return 0;
}
static int demo_driver_remove(struct platform_device *pdev)
{
printk("%s,%d: Enter\n",__func__,__LINE__);
return 0;
}
static struct class *demo_class;
static int demo_driver_init(void)
{
int ret;
printk("%s,%d: Enter\n",__func__,__LINE__);
demo_class = class_create(THIS_MODULE,"demo_class");
ret = PTR_ERR(demo_class);
if (IS_ERR(demo_class)){
printk("could not create sysfs files\n");
return ret;
}
ret = class_create_file(demo_class, &class_attr_control);
if (ret) {
printk("could not create sysfs files\n");
return ret;
}
return platform_driver_register(&demo_driver);
}
static void demo_driver_exit(void)
{
printk("%s,%d: Enter\n",__func__,__LINE__);
class_remove_file(demo_class, &class_attr_control);
class_destroy(demo_class);
platform_driver_unregister(&demo_driver);
return;
}
MODULE_LICENSE("GPL");
module_init(demo_driver_init);
module_exit(demo_driver_exit);
网友评论