104 kset:驱动的骨架

一、kobject的容器,体现设备驱动的层次关系

在这里插入图片描述

/**
 * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem.
 *
 * A kset defines a group of kobjects.  They can be individually
 * different "types" but overall these kobjects all want to be grouped
 * together and operated on in the same manner.  ksets are used to
 * define the attribute callbacks and other common events that happen to
 * a kobject.
 *
 * @list: the list of all kobjects for this kset
 * @list_lock: a lock for iterating over the kobjects
 * @kobj: the embedded kobject for this kset (recursion, isn't it fun...)
 * @uevent_ops: the set of uevent operations for this kset.  These are
 * called whenever a kobject has something happen to it so that the kset
 * can add new environment variables, or filter out the uevents if so
 * desired.
 */
struct kset {
	struct list_head list;
	spinlock_t list_lock;
	struct kobject kobj;
	const struct kset_uevent_ops *uevent_ops;
};

二、kset_create_and_add()函数

lib/kobject.c

负责产生一个 kset对象,并与 sysfs 中的一个目录项 关联起来。

/* name:设置 kset 和 kset 对应的目录项的名称。
 * uevent_ops:负责 kset 和用户空间通信的一系列函数接口。kobject 只能依靠上层 kset 对象来向用户空间发送消息。
 * 				用户空间的 mdev 或 udev 守护进程监测到 kset 发出的信息后,创建设备文件。
 * parent_obj:设置 kset对象 上一层节点。
 */
struct kset *kset_create_and_add(const char *name,
									const struct kset_uevent_ops *uevent_ops, 
									struct kobject *parent_kobj)
{
	struct kset *kset;
	int error;
	/* 第一大步:
	 * 创建一个kset,并初始化其一些成员。
	 * 详见下。
	 */
	kset = kset_create(name, uevent_ops, parent_kobj);
	if (!kset)
		return NULL;
	/* 第二大步:
	 * 注册 kset。
 	 */
	error = kset_register(kset);
	if (error) {
		kfree(kset);
		return NULL;
	}
	return kset;
}

1、kset_create()函数

lib/kobject.c

static struct kset *kset_create(const char *name,
								const struct kset_uevent_ops *uevent_ops,
								struct kobject *parent_kobj)
{
	struct kset *kset;
	int retval;

	kset = kzalloc(sizeof(*kset), GFP_KERNEL);
	if (!kset)
		return NULL;
	/* 设置 kobject 的名字 
	 * 此函数内部会调用 kobject_set_name_vargs ,101节有详细介绍
	 * 其实基本等价于 kobject_set_name_vargs 
	 */
	retval = kobject_set_name(&kset->kobj, "%s", name);
	if (retval) {
		kfree(kset);
		return NULL;
	}
	/* 注册 消息发送接口。
	 * uevent_ops 是调用函数时作为参数传递进来的。
	 * 以后详细介绍此接口。
	 */
	kset->uevent_ops = uevent_ops;
	/* 设置 kset 的上一层 kobject 节点。
	 * 作为参数传递进来。
	 */
	kset->kobj.parent = parent_kobj;
	/* 操作属性接口 */
	kset->kobj.ktype = &kset_ktype;
	kset->kobj.kset = NULL;

	return kset;
}

2、kset_register()函数

lib/kobject.c

int kset_register(struct kset *k)
{
	int err;

	if (!k)
		return -EINVAL;
	/* 其实就是初始化 kset->kobject 一些标志成员。
	 * 详见下。
	 */
	kset_init(k);
	/* 创建一个目录项
	 * 并将 kobject 和 这个目录项 kernfs_node 关联起来。
	 * 详见 101。
	 */
	err = kobject_add_internal(&k->kobj);
	if (err)
		return err;

	/* 此函数专门用来发送 驱动模型消息 到应用层。
	 * KOBJ_ADD 是消息类型,告诉应用层:新注册了一个 kset 或者 kobject
	 */
	kobject_uevent(&k->kobj, KOBJ_ADD);
	return 0;
}
(1) kset_init()函数

lib/kobject.c

void kset_init(struct kset *k)
{
	/* 初始化 kobject 一些标志成员。
	 * 详见 101。
	 */
	kobject_init_internal(&k->kobj);
	/* 用于以后将一系列 kobject 节点串联起来 */
	INIT_LIST_HEAD(&k->list);
	spin_lock_init(&k->list_lock);
}

三、驱动源码

相比于上一次实验:本次只是先创建一个 kset 节点,再在 此节点 下面创建一个 kobject(用于控制 led)。
观察 /sys 目录下的层次关系;并通过 kobject 来控制 led,验证我们实现的 操作接口 是否正常工作。

#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>

/* GPIO虚拟地址指针。
 * __iomem 表示此指针指向的地址位于 io空间
 */
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)
{
	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;

	ret = kstrtoint(buf, 10, &foo);
	if (ret < 0)
		return ret;

	return count;
}

static struct kobj_attribute foo_attribute =
	__ATTR(foo, 0664, foo_show, foo_store);


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;
}

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

static struct attribute *attrs[] = {
	&foo_attribute.attr,
	&led_attribute.attr,
	NULL,	/* need to NULL terminate the list of attributes */
};

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

static struct kset *example_kset;
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);

	
	/* 创建一个kset对象。
	 * 指定了名字。
	 * 第二个参数是向应用层发送消息的 ops。
	 * 第三个参数上层的 kobject 节点。
	 */
	example_kset = kset_create_and_add("kset_example", NULL, NULL);
	if (!example_kset)
		return -ENOMEM;

	/* 创建一个kobject对象。
	 * 并把上面创建的 kset 对象对应的 kobject 作为此 kobject 的上层节点。
	 * 此函数详见 101.
	 */
	led_kobj = kobject_create_and_add("led_kobject", &example_kset->kobj);
	if (!led_kobj)
		return -ENOMEM;

	/* 为kobject设置属性文件*/
	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);

	/*取消字符设备的注册*/
	kset_unregister(example_kset);
}

module_init(led_init);
module_exit(led_exit);

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

四、编译测试

/lib/modules/4.1.15 # 
/lib/modules/4.1.15 # ls /sys/
block     class     devices   fs        module
bus       dev       firmware  kernel    power
/lib/modules/4.1.15 # 
/lib/modules/4.1.15 # 
/lib/modules/4.1.15 # insmod test.ko 
/lib/modules/4.1.15 # 
/lib/modules/4.1.15 # ls /sys
block         dev           fs            module
bus           devices       kernel        power
class         firmware      kset_example
/lib/modules/4.1.15 # 
/lib/modules/4.1.15 # ls /sys/kset_example/
led_kobject
/lib/modules/4.1.15 # 
/lib/modules/4.1.15 # ls /sys/kset_example/led_kobject/
foo  led
/lib/modules/4.1.15 # echo 66 > random: nonblocking pool is initialized
/lib/modules/4.1.15 # echo 66 > /sys/kset_example/led_kobject/foo
/lib/modules/4.1.15 # 
/lib/modules/4.1.15 # cat /sys/kset_example/led_kobject/foo
66
/lib/modules/4.1.15 # 
/lib/modules/4.1.15 # cat /sys/kset_example/led_kobject/led 
123
/lib/modules/4.1.15 # echo 'on' > /sys/kset_example/led_kobject/led 
/lib/modules/4.1.15 # echo 'off' > /sys/kset_example/led_kobject/led 
/lib/modules/4.1.15 # 
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值