总结:创建设备文件的方法
设备文件属性指的是 /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 继续更