比如我们要给pcie控制器加一个供电控制,来控制从设备的供电,可以在控制器设备中通过device_create_file加一个属性文件来控制
ssize_t pcie_pwr_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct device_node *node = dev->of_node;
int pwr_gpio = of_get_named_gpio(node, "pwr-gpios", 0);
int value = gpio_get_value(pwr_gpio);
return sprintf(buf, "pcie pwr gpio%d = %d\n", pwr_gpio, value);
}
ssize_t pcie_pwr_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct device_node *node = dev->of_node;
int pwr_gpio = of_get_named_gpio(node, "pwr-gpios", 0);
gpio_set_value(pwr_gpio, buf[0]-'0');
dev_err(dev,"set pcie pwr gpio%d = %d",pwr_gpio, buf[0]-'0');
return count;
}
static DEVICE_ATTR(pwr_ctrl, 0660,pcie_pwr_show, pcie_pwr_store);
device_create_file(dev, &dev_attr_pwr_ctrl);
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;
}
就会在sysfs生成如下节点,便于操作
任何 kobject 在系统中注册,就会有一个目录在 sysfs 中被创建。这个目录是作为该 kobject 的父对象所在目录的子目录创建的。sysfs 中的顶层目录代表着内核对象层次的共同祖先;例如:某些对象属于某个子系统。
Sysfs 在与其目录关联的 kernfs_node 对象中内部保存一个指向实现目录的 kobject 的指针
kobject 的属性可在文件系统中以普通文件的形式导出。Sysfs 为属性定义了面向文件 I/O 操作的方法,以提供对内核属性的读写。一个简单的属性结构定义如下:
struct attribute {
char * name;
struct module *owner;
umode_t mode;
};
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr);
void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr);
一个单独的属性结构并不包含读写其属性值的方法。子系统最好为特定对象类型的属性定义自己的属性结构体和封装函数。在驱动中的数据结构组织方式如下
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);
};
int device_create_file(struct device *, const struct device_attribute *);
void device_remove_file(struct device *, const struct device_attribute *);
static DEVICE_ATTR(foo, S_IWUSR | S_IRUGO, show_foo, store_foo);等价于如下
static struct device_attribute dev_attr_foo = {
.attr = {
.name = "foo",
.mode = S_IWUSR | S_IRUGO,
.show = show_foo,
.store = store_foo,
},
};
上面是一个属性类型的具体实例,是实现了show和store的,那么什么地方来调用这个.show和.store呢?在应用层读取文件时,就会访问该kobject --> kobj_type -->sysfs_ops -->show;这个show来调用的device_attribute 的 show成员
drivers/base/core.c
#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct device_attribute *dev_attr = to_dev_attr(attr);
struct device *dev = kobj_to_dev(kobj);
ssize_t ret = -EIO;
if (dev_attr->show)
ret = dev_attr->show(dev, dev_attr, buf);
if (ret >= (ssize_t)PAGE_SIZE) {
printk("dev_attr_show: %pS returned bad count\n",
dev_attr->show);
}
return ret;
}
static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
struct device_attribute *dev_attr = to_dev_attr(attr);
struct device *dev = kobj_to_dev(kobj);
ssize_t ret = -EIO;
if (dev_attr->store)
ret = dev_attr->store(dev, dev_attr, buf, count);
return ret;
}
static const struct sysfs_ops dev_sysfs_ops = {
.show = dev_attr_show,
.store = dev_attr_store,
};
static struct kobj_type device_ktype = {
.release = device_release,
.sysfs_ops = &dev_sysfs_ops,
.namespace = device_namespace,
.get_ownership = device_get_ownership,
};
使用device_ktye来作为struct device的kobj的kobj_type成员
void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset;
kobject_init(&dev->kobj, &device_ktype);
...
}
通过kobject_add,形成kobject目录树
void device_add(struct device *dev)
{
kobject_add(&dev->kobj, dev->kobj.parent, NULL);
device_create_file(dev, &dev_attr_uevent);
...
}
调用以上两个接口,在sys目录生成设备文件
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
{
device_initialize(dev);
device_add(dev);
}
每一个kobject对象都会关联一个sysfs文件目录,kobj_type成员就对应了这个对象被操作的具体方法,比如device_ktype的sysfs_ops
include/linux/kobject.h
struct kobject {
/*这个kobject对象的名字*/
const char *name;
/*链表结点,让kset对象将kobject串起来进行管理*/
struct list_head entry;
/*该kobject对象的上层结点*/
struct kobject *parent;
/*该kobject对象所属于的kset对象*/
struct kset *kset;
/*该kobject对象的sysfs文件系统的相关操作和属性,重要*/
struct kobj_type *ktype;
/*该kobject对象在sysfs文件系统中所对应的文件目录*/
struct kernfs_node *sd; /* sysfs directory entry */
/*该kobject对象的引用次数*/
struct kref kref;
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
struct delayed_work release;
#endif
/*该kobject对象的初始化状态*/
unsigned int state_initialized:1;
/*表示kobject对象是否在sysfs下建立目录*/
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;
};
在lib/kobject.c中有kobject_create_and_add函数,此函数为构建kobject对象的入口 ,类似device_create
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
{
struct kobject *kobj;
int retval;
kobj = kobject_create();
if (!kobj)
return NULL;
retval = kobject_add(kobj, parent, "%s", name);
if (retval) {
pr_warn("%s: kobject_add error: %d\n", __func__, retval);
kobject_put(kobj);
kobj = NULL;
}
return kobj;
}
其中kobject_create()作用是创建并且初始化一个kobject对象,kobject_add(kobj, parent, "%s", name)则是在sysfs目录下创建一个目录项与该kobject对象进行关联
struct kobject *kobject_create(void)
{
struct kobject *kobj;
kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
if (!kobj)
return NULL;
kobject_init(kobj, &dynamic_kobj_ktype);
return kobj;
}
static struct kobj_type dynamic_kobj_ktype = {
.release = dynamic_kobj_release,
.sysfs_ops = &kobj_sysfs_ops,
};
const struct sysfs_ops kobj_sysfs_ops = {
.show = kobj_attr_show,
.store = kobj_attr_store,
};
/* default kobject attribute operations */
static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct kobj_attribute *kattr;
ssize_t ret = -EIO;
kattr = container_of(attr, struct kobj_attribute, attr);
if (kattr->show)
ret = kattr->show(kobj, kattr, buf);
return ret;
}
static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
struct kobj_attribute *kattr;
ssize_t ret = -EIO;
kattr = container_of(attr, struct kobj_attribute, attr);
if (kattr->store)
ret = kattr->store(kobj, kattr, buf, count);
return ret;
}
先动态分配内存,存放kobject对象,并调用kobject_init完成目录属性文件的操作接口设置,设置kobject对象的ktype为dynamic_kobj_ktype,设置dynamic_kobj_ktype中的sysfs_ops变量为kobj_sysfs_ops,设置kobj_sysfs_ops为show变量为kobj_attr_show,store变量为kobj_attr_store,这就是该kobject对象默认的属性文件操作接口,分别对应着读写操作
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
...
kobject_init_internal(kobj);
kobj->ktype = ktype;
return;
...
}
kref_init(&kobj->kref)出初始化该kobject对象的引用次数,INIT_LIST_HEAD(&kobj->entry)初始化链表指针,kobj->state_in_sysfs = 0表示该kobject读写还没有与sysfs文件目录相关联,kobj->state_initialized = 1代表kobject对象已经初始化了
static void kobject_init_internal(struct kobject *kobj)
{
if (!kobj)
return;
kref_init(&kobj->kref);
INIT_LIST_HEAD(&kobj->entry);
kobj->state_in_sysfs = 0;
kobj->state_add_uevent_sent = 0;
kobj->state_remove_uevent_sent = 0;
kobj->state_initialized = 1;
}
kobject_add函数,位于内核代码的lib/kobject.c下,将创建的目录项放进sysfs的目录树中
int kobject_add(struct kobject *kobj, struct kobject *parent,
const char *fmt, ...)
{
va_list args;
int retval;
...........
va_start(args, fmt);
retval = kobject_add_varg(kobj, parent, fmt, args);
va_end(args);
return retval;
}
注册sysfs文件系统
static struct file_system_type sysfs_fs_type = {
.name = "sysfs",
.init_fs_context = sysfs_init_fs_context,
.kill_sb = sysfs_kill_sb,
.fs_flags = FS_USERNS_MOUNT,
};
int __init sysfs_init(void)
{
int err;
sysfs_root = kernfs_create_root(NULL, KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK,
NULL);
if (IS_ERR(sysfs_root))
return PTR_ERR(sysfs_root);
sysfs_root_kn = sysfs_root->kn;
err = register_filesystem(&sysfs_fs_type);
if (err) {
kernfs_destroy_root(sysfs_root);
return err;
}
return 0;
}