01_kobject和ktype创建设备文件和设备目录

news2024/11/14 18:06:01

总结:创建设备文件的方法

设备文件属性指的是 /sys/yyy/xxx
yyy:代表这个设备的目录
xxx:代表这个驱动设备的各种属性,我们可以直接操控属性来控制这个设备
比如之前常见的 echo 5 > /sys/led/brightness 直接操作这个属性来更改led的亮度

1 创建设备kobj对象,绑定目录

kobject_create_and_add(“led_kobject”, NULL);
这个函数做了三件事情

  • 构建一个kobject对象
  • 构建一个sysfs中的目录项(kernfs_node)就是上面说的yyy,也是填入的led_kobject
  • 把他们关联起来

2 创建kobj对象属性

sysfs_create_group()
对第一步创建的kobj对象创建 多个kobj_arrt 每个attr绑定一个文件名
就像 /sys/led/brightness brightness是一个文件 绑定了kobj的属性

static struct kobj_attribute led_attribute =
	__ATTR(led, 0664, led_show, led_store);

static struct attribute *attrs[] = {
	&foo_attribute.attr,   //对应 /sys/led_kobject/foo
	&led_attribute.attr,   //对应 /sys/led_kobject/led
	NULL,	/* need to NULL terminate the list of attributes */
};
static struct attribute_group attr_group = {
	.attrs = attrs,
};
sysfs_create_group(led_kobj, &attr_group);

3 通过操作对象属性文件 操控设备

上面两步写在驱动中,注册完驱动后
在这里插入图片描述
会sys中创建文件目录(led_kobject)和属性文件(foo,led)
并且把驱动中创建的 kobj 和kobj_arrr 和文件绑定 使用文件操作接口就能进行操作

代码实战

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <asm/io.h>

#define DEV_MAJOR		0		/* 动态申请主设备号 */
#define DEV_NAME		"red_led" 	/*led设备名字 */

/* GPIO虚拟地址指针 
 * __iomem 表示:该指针是指向一个I/O的内存空间
 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

static int foo;

static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr,
			char *buf)
{
	/* 把这个驱动中的全局变量 保存到buf里面
	 *  buf 将会被自动拷贝到用户空间。
 	 *   内核帮执行过copy_to_usr
	 */
	return sprintf(buf, "%d\n", foo);
}

static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr,
			 const char *buf, size_t count)
{
	int ret;
	/* buf内容来自用户空间,由内核自动完成了。
	 * kstrtoint 是将子串buf以十进制的格式输出到foo。
	 */
	ret = kstrtoint(buf, 10, &foo);
	if (ret < 0)
		return ret;

	return count;
}

/* __ATTR 定义在 include/linux/sysfs.h。
 * show成员 和 store成员 最终分别会被 kobject->ktype 下的 kobj_sys_ops 下的。
 * kobj_attr_show 和 kobj_attr_store 调用。
 * foo 对应属性文件名。
 */
static struct kobj_attribute foo_attribute =
	__ATTR(foo, 0664, foo_show, foo_store);
/* __ATTR 定义详见下 */

static ssize_t led_show(struct kobject *kobj, struct kobj_attribute *attr,
		      char *buf)
{
	int var;

	if (strcmp(attr->attr.name, "led") == 0)
			var =123;

	return sprintf(buf, "%d\n", var);
}

static ssize_t led_store(struct kobject *kobj, struct kobj_attribute *attr,
		       const char *buf, size_t count)
{

	if (strcmp(attr->attr.name, "led") == 0){
		if(!memcmp(buf,"on",2)) {	
			iowrite32(0 << 3, GPIO1_DR);	
		} else if(!memcmp(buf,"off",3)) {
			iowrite32(1 << 3, GPIO1_DR);
		}
	}
	return count;
}

/* led 对应属性名 
led_show  对应open后的read 也对应cat
led_store 对应open后的write 也对应echo xx >/sys/led_kobject/led
*/
static struct kobj_attribute led_attribute =
	__ATTR(led, 0664, led_show, led_store);

/* 下面是attr一维数组的指针
	每个attr对应一个文件夹中的一个文件*/
static struct attribute *attrs[] = {
	&foo_attribute.attr,   //对应 /sys/led_kobject/foo
	&led_attribute.attr,   //对应 /sys/led_kobject/led
	NULL,	/* need to NULL terminate the list of attributes */
};


static struct attribute_group attr_group = {
	.attrs = attrs,
};

static struct kobject *led_kobj;

static int __init led_init(void)
{
	int retval;

	/* GPIO相关寄存器映射 */
  	IMX6U_CCM_CCGR1 = ioremap(0x20c406c, 4);
	SW_MUX_GPIO1_IO03 = ioremap(0x20e006c, 4);
  	SW_PAD_GPIO1_IO03 = ioremap(0x20e02f8, 4);
	GPIO1_GDIR = ioremap(0x0209c004, 4);
	GPIO1_DR = ioremap(0x0209c000, 4);


	/* 使能GPIO1时钟 */
	iowrite32(0xffffffff, IMX6U_CCM_CCGR1);

	/* 设置GPIO1_IO03复用为普通GPIO*/
	iowrite32(5, SW_MUX_GPIO1_IO03);
	
    /*设置GPIO属性*/
	iowrite32(0x10B0, SW_PAD_GPIO1_IO03);

	/* 设置GPIO1_IO03为输出功能 */
	iowrite32(1 << 3, GPIO1_GDIR);

	/* LED输出高电平 */
	iowrite32(1<< 3, GPIO1_DR);

	/*创建一个kobject对象led_kobj,并且父kernfs_node = NULL,
	  因为父kernfs_node = NULL 在sys/根目录下创建文件夹 led_kobj
	  把文件夹和kobject关联
	 */
	led_kobj = kobject_create_and_add("led_kobject", NULL);
	if (!led_kobj)
		return -ENOMEM;

/* 给这个kobj创建创建属性attr
	同时会在/sys/led_kobj目录下生成属性文件
	attr_group结构体里有attrs的双重指针,给每个attrs创建操作文件
*/
	retval = sysfs_create_group(led_kobj, &attr_group);
	if (retval)
		kobject_put(led_kobj);

	return retval;

	return 0;
}

static void __exit led_exit(void)
{
	/* 取消映射 */
	iounmap(IMX6U_CCM_CCGR1);
	iounmap(SW_MUX_GPIO1_IO03);
	iounmap(SW_PAD_GPIO1_IO03);
	iounmap(GPIO1_DR);
	iounmap(GPIO1_GDIR);

	/* 注销字符设备驱动 */
	kobject_put(led_kobj);
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("embedfire ");
MODULE_DESCRIPTION("led_module");
MODULE_ALIAS("led_module");

详细心得

打开yehuo 102的图片配合使用

sys/led_kobject 如何创建如何映射驱动内容

这节讲的是目录映射

kn = kernfs_node = kobj->sd =   sys里的目录


一个kobjet对象就关联一个 sys/下面的目录项??
通过kobject默认的属性文件操作接口   找到文件在的sys/下面的目录项   再通过这个目录项找到kobject对象
通过读写这个文件 操作这个kobject对象


kobject_create_and_add()函数  做了下面三个事情
	- 构建一个kobject对象 
	- 构建一个sysfs中的目录项(kernfs_node)
	- 把他们关联起来
	
kobject_create_and_add()
	/*创建并初始化一个kobject对象*/	
	kobj = kobject_create();
		kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
		kobject_init(kobj, &dynamic_kobj_ktype);  //初始化 kobj_ktype
			static struct kobj_type dynamic_kobj_ktype = {
			.release	= dynamic_kobj_release,
			.sysfs_ops	= &kobj_sysfs_ops,
			};
			const struct sysfs_ops kobj_sysfs_ops = {  //下面两是kob统一的属性文件读写接口 
			.show	= kobj_attr_show,
			.store	= kobj_attr_store,
			};
	/*sysfs创建一个目录项并与kobject对象关联*/
	retval = kobject_add(kobj, parent, "%s", name);
		kobject_add_varg()
			kobject_add_internal()
				create_dir()
					sysfs_create_dir_ns()  
						if (kobj->parent)
						parent = kobj->parent->sd; 如果有上层节点,设置上层节点
						else
						parent = sysfs_root_kn;  //没有上层节点,parent为sys根目录
						kn = kernfs_create_dir_ns()  //这里看kn就是sys里的目录
							kn = kernfs_new_node()
							kn->priv = priv; //sysfs目录项关联kobject对象
						kobj->sd = kn;  //kobj关联sysfs目录项

/sys/led_kobject/led 如何创建,如何映射驱动内容

视频 kobj_type 对象用户空间的法宝  这节讲的是文件映射属性

- 为kobject对象构建多个属性文件
- 为每个属性文件设置具体操作接口
- vfs的inode对象与sysfs的kernfs_node对象的绑定过程

		kobject_init(kobj, &dynamic_kobj_ktype);  //初始化 kobj_ktype
			static struct kobj_type dynamic_kobj_ktype = {
			.release	= dynamic_kobj_release,
			.sysfs_ops	= &kobj_sysfs_ops,
			};
			const struct sysfs_ops kobj_sysfs_ops = {  //下面两是kob统一的属性文件读写接口 
			.show	= kobj_attr_show,
			.store	= kobj_attr_store,
			};
有了统一的属性文件操作接口,我们可以在用户空间 通过kobj对象属性文件,控制kobj对象 

所有的属性文件共用  .show store接口,这两个文件 再去调用每个属性具体的操作接口
下面三个结构体 一个kobj_attribute 有操作接口 show store(这两个指针真正操作这个属性文件) 同时存了attribute 的name 和操作权限mode
struct attribute_group {
	const char		*name;
	umode_t			(*is_visible)(struct kobject *,
					      struct attribute *, int);
	umode_t			(*is_bin_visible)(struct kobject *,
						  struct bin_attribute *, int);
	struct attribute	**attrs;
	struct bin_attribute	**bin_attrs;
};
struct kobj_attribute {
	struct attribute attr;
	ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
			char *buf);
	ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
			 const char *buf, size_t count);
};
struct attribute {
	const char		*name;
	umode_t			mode;
};

函数分析
sysfs_create_group()
	internal_create_group()
		kn = kobj->sd;  //获取kobj的目录项
		create_files(kn, kobj, uid, gid, grp, update); //创建文件要目录项节点(kn),kobj,grop等
			for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++)
				sysfs_add_file_mode_ns(parent, *attr, false,mode, uid, gid, NULL); //根据每一个attribute 创建文件
					struct kobject *kobj = parent->priv;  //获取这个目录对应的kobj
					const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops;  //上一步为了获取这个kobj的ktype的sysfs_ops 
					if (sysfs_ops->show && sysfs_ops->store)
						ops = &sysfs_file_kfops_rw;//如果kobj_ytpe的sysfs_ops不为空 初始化准备创建的文件的ops
					kn = __kernfs_create_file(parent, attr->name, mode & 0777, uid, gid,size, ops, (void *)attr, ns, key);  真正创建这个文件
						文件在的目录 = parent(也就是ks这个目录节点)	attr->name = 文件名字  mode= 文件权限  
						kn = kernfs_new_node(parent, name, (mode & S_IALLUGO) | S_IFREG,uid, gid, flags); //创建的新kernfs_node节点,给属性文件用
						/*操作接口赋值*/
						kn->attr.ops = ops;
						kn->attr.size = size;
						kn->ns = ns;
						/*文件属性赋值*/
						kn->priv = priv;

总结 这里表示了 驱动图片中 下半部分是啥 下半部分为文件创建  上半部分为目录创建
kernfs_node结构体 又能表示文件 又能表示目录
下半部分中一个文件 也对应了一个结构体 kernfs_node  
kernfs_node->priv 就是kobj_attribute(文件属性结构体)
有三个ops->show函数 目录show 文件show  文件->priv->show
目录show  初始化 kobject_create_and_add()
文件show  sysfs_create_group() if (sysfs_ops->show && sysfs_ops->store)如果有目录show  ops = &sysfs_file_kfops_rw;
文件->priv->show  = kobj_attr->show   老师说在应用程序里面才初始化 这是在驱动里自己实现的函数 最后被kboj->ktype->sysfs_ops->show

上面是创建了 sys文件节点 但是没有和 vfs的inode关联
关联的时候 是应用程序在调用opan函数时候
kernfs_init_inode(struct kernfs_node *kn, struct inode *inode) //两个参数,sys创建的文件节点,虚拟文件系统inode节点
	/*sysfs的kernels_node赋值给vfs的inode*/
	inode->i_private = kn;
	case KERNFS_FILE:
		inode->i_size = kn->attr.size;
		/*文件的操作接口*/
		inode->i_fop = &kernfs_file_fops;   //先用官方的操作接口赋值

const struct file_operations kernfs_file_fops = {  //官方的操作接口,基本赋值
	.read		= kernfs_fop_read,
	.write		= kernfs_fop_write,
	.llseek		= generic_file_llseek,
	.mmap		= kernfs_fop_mmap,
	.open		= kernfs_fop_open,
	.release	= kernfs_fop_release,
	.poll		= kernfs_fop_poll,
	.fsync		= noop_fsync,
};
//因为赋值的是官方open 所以打开就运行下面的这个open
kernfs_fop_open()    还没更完102 继续更

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/176518.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言中不定参数 ... 的语法、函数封装

文章目录Intro语法测试依赖库新函数使用测试&#xff1a;遍历并打印不定参数中的值用两种方式封装函数&#xff1a;对多个int值求和总结Intro 有一天看C代码看到某个方法有这样的定义&#xff1a;在函数形参列表处&#xff0c;有...的写法&#xff0c;就像Java中的不定参数那样…

JDK8 新特性之Stream流方法详解

目录 一&#xff1a;集合处理数据的弊端 二&#xff1a;Stream流式思想概述 小结 &#xff1a; 三&#xff1a;获取Stream流的两种方式 方式1 : 根据Collection获取流 方式2 : Stream中的静态方法of获取流 小结 四&#xff1a;Stream常用方法和注意事项 Stream常用方法…

19. 函数基础知识详解

1. 什么是函数 函数是组织好的&#xff0c;可重复使用的&#xff0c;用来实现单一&#xff0c;或相关联功能的代码段。函数能提高应用的模块性&#xff0c;和代码的重复利用率。之前文章中我们已经使用过python提供的内建函数&#xff0c;比如print()。但你也可以自己创建函数…

【JUC并发编程】线程池及相关面试题 详解

【JUC并发编程】线程池及相关面试题 详解 参考资料&#xff1a; 第十二章 线程池原理 深入浅出Java多线程原理 两道面试题&#xff0c;深入线程池&#xff0c;连环17问 深入理解Java并发编程之线程池、工作原理、复用原理及源码分析 硬核干货&#xff1a;4W字从源码上分析JUC…

Java二叉树OJ题

目录1. 检查两颗树是否相同2. 另一颗树的子树3. 翻转二叉树4. 判断一颗二叉树是否是平衡二叉树4.1 时间复杂度为O(n*n)【从上而下递归】4.2 时间复杂度为O(n)【从下而上递归】5. 对称二叉树6. 二叉树的构建及遍历7. 二叉树创建字符串8. 两个指定节点的最近公共祖先8.1 指定结点…

java入门作业-DAO,读取sql数据库

DAO意思是数据库、访问、对象。有了前后端思想。 需要下载mysql&#xff0c;并下载链接自己设置好密码。可以在navicat等软件链接打开。 材料是jc0122.sql&#xff0c;里面的admin_info是要操作的表。不像上一节需要把数据库文件放到java目录里。数据库只要在本地即可。 一、…

浅析Spring的五大类注解和方法注解

简单的将bean对象存储到Spring容器中&#xff0c;可以使用五大类注解实现&#xff0c;也可以通过Bean方法注解实现。本文重点围绕这几个问题展开&#xff1a;1.为什么需要五大类注解&#xff1f;2.五大类注解之间有没有关系&#xff1f;3.Spring使用五大类注解生成beanName问题…

Linux常见命令 17 - 帮助命令 man,whatis,--help,info,help

目录 1. 查看命令/配置文件帮助信息 man 2. 查看简短的命令帮助信息 whatis 3. 查看简短的命令选项信息 --help 4. 另一查看命令文件帮助信息 info 5. Shell内置帮助命令 help 1. 查看命令/配置文件帮助信息 man [1] 语法&#xff1a;man [命令] 示例&#xff1a;如果想要查…

分享129个ASP源码,总有一款适合您

ASP源码 分享129个ASP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 129个ASP源码下载链接&#xff1a;https://pan.baidu.com/s/1dUPOSf1BudsK-bB4FnGXfQ?pwdg3ae 提取码&#x…

黑马2021-8Vue自学笔记hm

黑马2021-8Vue教程学习笔记 文章目录黑马2021-8Vue教程学习笔记代码和笔记不断更新gitee代码仓库地址备用前端gei忽略提交文件webpack的使用初始化包管理配置文件 package.json下载webpack解决问题dependencies 和 devDependencies区别:在项目中配置webpackWebpack 中的默认约定…

python基础——列表推导式

python基础——列表推导式 文章目录python基础——列表推导式一、实验目的二、实验原理三、实验环境四、实验内容五、实验步骤一、实验目的 掌握Python数据结构&#xff1a;列表推导式的用法。 二、实验原理 列表推导式&#xff08;list comprehension&#xff09;列表推导式…

【Python百日进阶-Web开发-Linux】Day235 - Win11的WSL2中安装Docker

文章目录一、Docker是什么二、Win11安装Docker2.1 官网下载2.2 系统要求&#x1f517;2.2.1 WSL 2 后端2.2.2 Hyper-V 后端和 Windows 容器2.2.3 关于 Windows 容器2.3 在 Windows 上安装 Docker Desktop2.3.1 交互式安装2.3.2 从命令行安装2.4 启动 Docker 桌面三、WSL2的Ubun…

ConcurrentHashMap的死循环问题

文章目录前言1. 情景复现2. 源码解析3. 代码调试4. 原因5. 解决前言 对于ConcurrentHashMap来说&#xff0c;能保证多线程下的安全问题&#xff0c;但是在JDK1.8中还是会出现一个bug&#xff0c;就是computeIfAbsent&#xff0c;下面就来详细说说死循环的原因 1. 情景复现 首…

进阶C语言 第二章-------《进阶指针》 (指针数组、数组指针、函数指针、回调指针)知识点+基本练习题+深入细节+通俗易懂+完整思维导图+建议收藏

绪论 书接上回&#xff0c;通过对数据类型进阶的认识&#xff0c;你肯定对各种数据类型在内存中如何存储有了了解。虽然说&#xff0c;这方面可能对你的编程能力没什么进步。但是&#xff0c;他是一本内功秘籍&#xff0c;当我们遇到了这方面的问题时我们可以知道可能是哪一方面…

使用 Grafana 请求API接口

目的: 使用Grafana 配合JSON API 插件 请求API接口,完成可视化,实现一些简单的请求功能 假设我们想将如下的API接口返回的json数据可视化 这里借用一下 小熊同学的 金融数据接口 用请求如下接口举例 https://api.doctorxiong.club/v1/fund/detail?code000001&startDat…

色环电容读数方法要点总结

🏡《电子元器件学习目录》 目录 1,概述2,读数方法3,颜色对照表3.1,颜色与电容值数字对照关系表3.2,颜色与10的指数数字对照关系表3.3,颜色与误差对照关系表4,总结1,概述 本文简单介绍色环电容的读数方法。 2,读数方法 如下图所示色环电容共有四个色环。最粗的被命名…

36.Isaac教程--复合消息

复合消息 ISAAC教程合集地址: https://blog.csdn.net/kunhe0512/category_12163211.html 文章目录复合消息测量类型使用复合消息的示例手臂关节速度基本轨迹命令手臂关节和末端执行器命令CompositeMetric 和 CompositeAtlas组件使用 CompositeMetric 计算距离在 Python 脚本中创…

使用bookdown构建新年日记本

简介 一年多前在 B 站发布了一个视频&#xff0c;预览了一下基于 bookdown 构建的日记本。本打算之后更个文字版本教程。结果一直忘了&#xff0c;最近通过一位读者的了解&#xff0c;打算把这个坑补上。 本文内容将展示如何使用 bookdown 模板并修改成自己的日记本。此外&…

某阿里员工提问:年终绩效自己给自己打3.25,会有什么后果?网友回答:必死无疑,不要犯傻!...

年底评绩效&#xff0c;大多数人都会给自己打高绩效&#xff0c;但有些人却反其道而行之。最近&#xff0c;一位阿里员工提出了这样的问题&#xff1a;自评绩效时给自己打3.25&#xff0c;会有什么后果&#xff1f;希望领导能不按常理出牌&#xff0c;给自己3.75。有人问&#…

HashMap1.7中的线程安全问题

文章目录前言正文前言 下面聊聊JDK1.7HashMap的死循环问题&#xff0c;在这之前首先要知道JDK1.7的HashMap底层是数组 链表的形式的 正文 下面给出JDK1.7的扩容代码 //扩容代码 void resize(int newCapacity) {//旧的数组Entry[] oldTable table;//旧的数组长度int oldCap…