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

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

设备文件属性指的是 /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 继续更
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值