Linux创建sysfs属性节点 - DEVICE_ATTR宏、device_create_file()、sysfs_create_group()

2024-09-02 04:20

本文主要是介绍Linux创建sysfs属性节点 - DEVICE_ATTR宏、device_create_file()、sysfs_create_group(),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

简介:

一、DEVICE_ATTR介绍

1、DEVICE_ATTR宏

1.1 参数说明

1.2 调用方法

二、sysfs创建属性文件

1、创建一个sysfs属性文件

1.1 device_create_file()函数

1.2 device_create_file()实例

2、创建多个sysfs属性文件

2.1 sysfs_create_group()函数

2.2 sysfs_create_group()实例


简介:

在Linux驱动调试时,常常需要添加属性文件,sysfs属性节点可以实现用户空间与硬件交互,如:设置GPIO管脚电平、控制驱动等功能。下面介绍如何创建sysfs属性节点。

一、DEVICE_ATTR介绍


1、DEVICE_ATTR宏


DEVICE_ATTR 宏在linux/device.h中有如下定义:

/* 路径:linux/device.h */
#define DEVICE_ATTR(_name, _mode, _show, _store) \struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)struct device_attribute {struct attribute	attr;ssize_t (*show)(struct device *dev, struct device_attribute *attr,char *buf);ssize_t (*store)(struct device *dev, struct device_attribute *attr,const char *buf, size_t count);
};/* 路径:include/linux/sysfs.h */
#define __ATTR(_name, _mode, _show, _store) {				\.attr = {.name = __stringify(_name),				\.mode = VERIFY_OCTAL_PERMISSIONS(_mode) },		\.show	= _show,						\.store	= _store,						\
}

DEVICE_ATTR 宏用来定义一个 struct device_attribute 结构体,并对各成员初始化。

1.1 参数说明

DEVICE_ATTR(_name, _mode, _show, _store)
  • _name:名称,也就是将在sysfs中生成的文件名称;
  • _mode:属性文件的权限mode,与普通文件相同,UGO的格式。只读0444,只写0222,或者读写都行的0666;
  • _show:显示函数,cat该文件时,此函数被调用;
  • _store:写函数,echo内容到该文件时,此函数被调用;

1.2 调用方法

static DEVICE_ATTR(demo, 0664, demo_show, demo_store);

demo_show、demo_store函数由我们自己定义,展开后

struct device_attribute dev_attr_demo = {.attr = {.name = __stringify(demo),.mode = VERIFY_OCTAL_PERMISSIONS(0664) },.show	= demo_show,.store	= demo_store,
}

调用 DEVICE_ATTR 后,就将 device_attribute 结构体初始化完成了。还需要使用 device_create_file() sysfs_create_group() 将属性文件加入sysfs文件系统中。

二、sysfs创建属性文件


1、创建一个sysfs属性文件


1.1 device_create_file()函数

DEVICE_ATTR 宏创建 device_attribute 结构体后,调用 device_create_file() 将属性文件加入sysfs文件系统中,会在 /sys/class/xxx 子目录下生成一个属性文件。

/* 路径:drivers/base/core.c */
int device_create_file(struct device *dev,const struct device_attribute *attr)
{int error = 0;if (dev) {WARN(((attr->attr.mode & S_IWUGO) && !attr->store),"Attribute %s: write permission without 'store'\n",attr->attr.name);WARN(((attr->attr.mode & S_IRUGO) && !attr->show),"Attribute %s: read permission without 'show'\n",attr->attr.name);error = sysfs_create_file(&dev->kobj, &attr->attr);}return error;
}
EXPORT_SYMBOL_GPL(device_create_file);

这种方式一次只能创建一个属性节点。

1.2 device_create_file()实例

使用 device_create_file() 函数时要引用 device_create 创建设备时返回的device*指针,作用是在/sys/class/下创建一个属性文件,从而通过对这个属性文件进行读写就能完成对应的数据操作。

实例代码:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>typedef struct
{dev_t devid; 			/* 设备号 */int major;				/* 主设备号 */int minor;				/* 次设备号 */struct cdev cdev;   	/* cdev */struct class *class;	/* 类 */struct device *device;  /* 设备 */
}CHARDEV_HANDLE_T;#define CDEV_CNT		1		  		/* 设备号个数 */
#define CDEV_NAME		"my_leddrv"		/* 名字 */static CHARDEV_HANDLE_T stMyled = {0};static int led_drv_open(struct inode *node, struct file *file)
{//filp->private_data = &cdev_data;  /* 设置私有数据 */printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}static int led_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}static int led_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);return 1;
}static int led_drv_release(struct inode *node, struct file *file)
{printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}/* 设备操作函数 */
static struct file_operations led_drv_fops =
{.owner = THIS_MODULE,.open  = led_drv_open,.read  = led_drv_read,.write = led_drv_write,.release = led_drv_release,
};ssize_t para_show(struct device *dev, struct device_attribute *attr, char *buf)
{printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);return sprintf(buf, "hello world!\n");
}ssize_t para_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);printk(KERN_INFO "%s\n", buf);return count;
}static DEVICE_ATTR(para, 0664, para_show, para_store);/* 入口函数 */
static int __init led_init(void)
{printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
/* 注册字符设备驱动 *//* 1、创建设备号 */if (stMyled.major) { /* 定义了设备号 */stMyled.devid = MKDEV(stMyled.major, 0);register_chrdev_region(stMyled.devid, CDEV_CNT, CDEV_NAME);} else { /* 没有定义设备号 */alloc_chrdev_region(&stMyled.devid, 0, CDEV_CNT, CDEV_NAME); /* 申请设备号 */stMyled.major = MAJOR(stMyled.devid); /* 获取分配号的主设备号 */stMyled.minor = MINOR(stMyled.devid); /* 获取分配号的次设备号 */}printk("major=%d, minor=%d\r\n", stMyled.major, stMyled.minor);/* 2、初始化 cdev */stMyled.cdev.owner = THIS_MODULE;cdev_init(&stMyled.cdev, &led_drv_fops);				//file_operations/* 3、添加一个 cdev */cdev_add(&stMyled.cdev, stMyled.devid, CDEV_CNT);/* 4、创建类 */stMyled.class = class_create(THIS_MODULE, CDEV_NAME);	///sys/class/目录下会创建一个新的文件夹if (IS_ERR(stMyled.class)) {return PTR_ERR(stMyled.class);}/* 5、创建设备 */stMyled.device = device_create(stMyled.class, NULL, stMyled.devid, NULL, CDEV_NAME);//dev目录下创建相应的设备节点if (IS_ERR(stMyled.device)) {return PTR_ERR(stMyled.device);}/* 6、将属性文件加入sysfs文件系统中 */device_create_file(stMyled.device, &dev_attr_para);return 0;
}/* 出口函数 */
static void __exit led_exit(void)
{printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);/* 注销字符设备驱动 */cdev_del(&stMyled.cdev); /* 删除 cdev */unregister_chrdev_region(stMyled.devid, CDEV_CNT); /* 注销 */device_destroy(stMyled.class, stMyled.devid);class_destroy(stMyled.class);/* 移除sysfs中的device属性节点 *///device_remove_file(stMyled.device, &dev_attr_para);
}module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("donga");

Makefile编译,生成 .ko 文件

KERN_DIR = /home/linux-imx-rel_imx_4.1.15_2.1.0all:make -C $(KERN_DIR) M=`pwd` modulesclean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderobj-m   += chardev_drv.o

insmod加载 .ko 驱动后,生成 /sys/class/my_leddrv/my_leddrv/ 下有para属性节点。cat命令会调用 .show 函数,echo 会调用 .store 函数。测试结果如下:

# cat /sys/class/my_leddrv/my_leddrv/para

# echo "DEVICE_ATTR test" > /sys/class/my_leddrv/my_leddrv/para

2、创建多个sysfs属性文件


2.1 sysfs_create_group()函数

对于多个属性文件的添加,我们可以定义属性组,然后将这个属性组使用 sysfs_create_group() 添加进sysfs文件系统中。

int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp)

2.2 sysfs_create_group()实例

关键步骤:

static DEVICE_ATTR(para1, 0664, para_show, para_store);
static DEVICE_ATTR(para2, 0664, para_show, para_store);
static DEVICE_ATTR(para3, 0664, para_show, para_store);static struct attribute *para_attribute[] =
{&dev_attr_para1.attr,&dev_attr_para2.attr,&dev_attr_para3.attr,NULL,
};static struct attribute_group para_attribute_group =
{.attrs = para_attribute,
};sysfs_create_group(&myled.device->kobj, &para_attribute_group);

上面关键步骤我为了简单各个属性文件的show函数和store函数都一样,大家可以根据实际情况自行分别定义。

示例如下:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>typedef struct
{dev_t devid; 			/* 设备号 */int major;				/* 主设备号 */int minor;				/* 次设备号 */struct cdev cdev;   	/* cdev */struct class *class;	/* 类 */struct device *device;  /* 设备 */
}CHARDEV_HANDLE_T;#define CDEV_CNT		1		  		/* 设备号个数 */
#define CDEV_NAME		"my_leddrv"		/* 名字 */static CHARDEV_HANDLE_T stMyled = {0};static int led_drv_open(struct inode *node, struct file *file)
{//filp->private_data = &cdev_data;  /* 设置私有数据 */printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}static int led_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}static int led_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);return 1;
}static int led_drv_release(struct inode *node, struct file *file)
{printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}/* 设备操作函数 */
static struct file_operations led_drv_fops =
{.owner = THIS_MODULE,.open  = led_drv_open,.read  = led_drv_read,.write = led_drv_write,.release = led_drv_release,
};ssize_t para_show(struct device *dev, struct device_attribute *attr, char *buf)
{printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);return sprintf(buf, "hello world!\n");
}ssize_t para_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);printk(KERN_INFO "%s\n", buf);return count;
}static DEVICE_ATTR(para1, 0664, para_show, para_store);
static DEVICE_ATTR(para2, 0664, para_show, para_store);
static DEVICE_ATTR(para3, 0664, para_show, para_store);static struct attribute *para_attribute[] =
{&dev_attr_para1.attr,&dev_attr_para2.attr,&dev_attr_para3.attr,NULL,
};static struct attribute_group para_attribute_group =
{.attrs = para_attribute,
};/* 入口函数 */
static int __init led_init(void)
{printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
/* 注册字符设备驱动 *//* 1、创建设备号 */if (stMyled.major) { /* 定义了设备号 */stMyled.devid = MKDEV(stMyled.major, 0);register_chrdev_region(stMyled.devid, CDEV_CNT, CDEV_NAME);} else { /* 没有定义设备号 */alloc_chrdev_region(&stMyled.devid, 0, CDEV_CNT, CDEV_NAME); /* 申请设备号 */stMyled.major = MAJOR(stMyled.devid); /* 获取分配号的主设备号 */stMyled.minor = MINOR(stMyled.devid); /* 获取分配号的次设备号 */}printk("major=%d, minor=%d\r\n", stMyled.major, stMyled.minor);/* 2、初始化 cdev */stMyled.cdev.owner = THIS_MODULE;cdev_init(&stMyled.cdev, &led_drv_fops);				//file_operations/* 3、添加一个 cdev */cdev_add(&stMyled.cdev, stMyled.devid, CDEV_CNT);/* 4、创建类 */stMyled.class = class_create(THIS_MODULE, CDEV_NAME);	///sys/class/目录下会创建一个新的文件夹if (IS_ERR(stMyled.class)) {return PTR_ERR(stMyled.class);}/* 5、创建设备 */stMyled.device = device_create(stMyled.class, NULL, stMyled.devid, NULL, CDEV_NAME);//dev目录下创建相应的设备节点if (IS_ERR(stMyled.device)) {return PTR_ERR(stMyled.device);}/* 6、将属性文件加入sysfs文件系统中 */sysfs_create_group(&stMyled.device->kobj, &para_attribute_group);return 0;
}/* 出口函数 */
static void __exit led_exit(void)
{printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);/* 注销字符设备驱动 */cdev_del(&stMyled.cdev); /* 删除 cdev */unregister_chrdev_region(stMyled.devid, CDEV_CNT); /* 注销 */device_destroy(stMyled.class, stMyled.devid);class_destroy(stMyled.class);
}module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("donga");

加载 .ko 文件后,在 /sys/class/my_leddrv/my_leddrv 目录下多了 para1、para2、para3。

使用 cat、echo命令测试:

# cat /sys/class/my_leddrv/my_leddrv/para

# echo 1 > /sys/class/my_leddrv/my_leddrv/para


这篇关于Linux创建sysfs属性节点 - DEVICE_ATTR宏、device_create_file()、sysfs_create_group()的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1128974

相关文章

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

day-51 合并零之间的节点

思路 直接遍历链表即可,遇到val=0跳过,val非零则加在一起,最后返回即可 解题过程 返回链表可以有头结点,方便插入,返回head.next Code /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}*

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

滚雪球学Java(87):Java事务处理:JDBC的ACID属性与实战技巧!真有两下子!

咦咦咦,各位小可爱,我是你们的好伙伴——bug菌,今天又来给大家普及Java SE啦,别躲起来啊,听我讲干货还不快点赞,赞多了我就有动力讲得更嗨啦!所以呀,养成先点赞后阅读的好习惯,别被干货淹没了哦~ 🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎大家关注&&收藏!持续更新中,up!up!up!! 环境说明:Windows 10