Linux设备模型(八) - sysfs

一,sysfs目录介绍

sysfs是一个基于内存的虚拟的文件系统,有kernel提供,挂载到/sys目录下,负责以设备树的形式向user space提供直观的设备和驱动信息。

sysfs以不同的视角展示当前系统接入的设备:

/sys/block 历史遗留问题,存放块设备,提供一设备名(如sda)到/sys/devices的符号链接;

/sys/bus 按总线类型分类,在某个总线目录之下可以找到链接该总线的设备的符号链接,指向/sys/devices. 某个总线目录之下的drivers目录包含了该总线所需的所有驱动的符号链接。对应kernel中的struct bus_type;

/sys/calss 按设备功能分类,如输入设备在/sys/class/input之下,图形设备在/sys/class/graphics之下,是指向/sys/devices的符号链接。 对应kernel中的struct class

/sys/dev 按设备驱动程序分层(字符设备 块设备),提供以major:minor为名到/sys/devices的符号链接。 对应Kernel中的struct device_driver;

/sys/devices 包含所有被发现的注册在各种总线上的各种物理设备。 所有物理设备都按其在总线上的拓扑结构来显示,除了platform devices和system devices。 platform devices 一般是挂载在芯片内部高速或者低速总线上的各种控制器和外设,能被CPU直接寻址。 system devices不是外设,而是芯片内部的核心结构,比如CPU,timer等。对应kernel中的strcut device;

/sys/firmware 提供对固件的查询和操作接口(关于固件有专用于固件加载的一套api);

/sys/fs 描述当前加载的文件系统,提供文件系统和文件系统已挂载的设备信息;

/sys/kernel 提供kernel所有可调整参数,但大多数可调整参数依然存放在sysctl(/proc/sys/kernel);

/sys/module 所有加载模块(包括内联、编译进kernel、外部的模块)信息,按模块类型分类;

/sys/power 电源选项,可用于控制整个机器的电源状态,如写入控制命令进行关机、重启等;

sysfs支持多视角查看,通过符号链接,同样的信息可出现在多个目录下。

以硬盘sda为例,既可以在块设备目录/sys/block下找到,又可以在/sys/devices/pci0000:00/0000:00:10.0/host32/target32:0:0/下找到。

二,sysfs与kobject的关系

sysfs的信息来源是kobject层次结构,读一个sysfs文件,就是动态的从kobject结构提取信息,生成文件。

每一个Kobject,都会对应sysfs中的一个目录。因此在将Kobject添加到Kernel时,create_dir接口会调用sysfs文件系统的创建目录接口,创建和Kobject对应的目录,相关的代码如下:

static int create_dir(struct kobject *kobj)
{
    const struct kobj_type *ktype = get_ktype(kobj);
    const struct kobj_ns_type_operations *ops;
    int error;

    error = sysfs_create_dir_ns(kobj, kobject_namespace(kobj)); //创建dir
    if (error)
        return error;

    error = populate_dir(kobj); //使用default attributes填充dir
    if (error) {
        sysfs_remove_dir(kobj);
        return error;
    }

    if (ktype) {
        error = sysfs_create_groups(kobj, ktype->default_groups); //使用default_groups填充dir
        if (error) {
            sysfs_remove_dir(kobj);
            return error;
        }
    }
    ... ...
}

/**
* sysfs_create_dir_ns - create a directory for an object with a namespace tag
* @kobj: object we're creating directory for
* @ns: the namespace tag to use
*/
int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
{
    struct kernfs_node *parent, *kn;
    kuid_t uid;
    kgid_t gid;

    if (WARN_ON(!kobj))
        return -EINVAL;

    if (kobj->parent)
        parent = kobj->parent->sd;
    else
        parent = sysfs_root_kn;

    if (!parent)
        return -ENOENT;

    kobject_get_ownership(kobj, &uid, &gid);

    kn = kernfs_create_dir_ns(parent, kobject_name(kobj),
                  S_IRWXU | S_IRUGO | S_IXUGO, uid, gid,
                  kobj, ns); //调用文件系统操作接口创建dir
    if (IS_ERR(kn)) {
        if (PTR_ERR(kn) == -EEXIST)
            sysfs_warn_dup(parent, kobject_name(kobj));
        return PTR_ERR(kn);
    }

    kobj->sd = kn;
    return 0;
}

三,attribute

1,attribute功能概述

所谓的attibute,就是内核空间和用户空间进行信息交互的一种方法。例如某个driver定义了一个变量,却希望用户空间程序可以修改该变量,以控制driver的运行行为,那么就可以将该变量以sysfs attribute的形式开放出来。

Linux内核中,attribute分为普通的attribute和二进制attribute,如下:

msm_kernel\include\linux\sysfs.h
struct attribute {
    const char        *name;
    umode_t            mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
    bool            ignore_lockdep:1;
    struct lock_class_key    *key;
    struct lock_class_key    skey;
#endif
};

struct bin_attribute {
    struct attribute    attr;
    size_t            size;
    void            *private;
    ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *,
            char *, loff_t, size_t);
    ssize_t (*write)(struct file *, struct kobject *, struct bin_attribute *,
             char *, loff_t, size_t);
    int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr,
            struct vm_area_struct *vma);
};

struct attribute为普通的attribute,使用该attribute生成的sysfs文件,只能用字符串的形式读写。而struct bin_attribute在struct attribute的基础上,增加了read、write等函数,因此它所生成的sysfs文件可以用任何方式读写。

2,attribute文件创建

/sys/bus/i2c # cat /sys/bus/i2c/drivers_autoprobe
1

有这样一个sysfs node,当bus register的时候,drivers_autoprobe node会被创建出来,用来表示 device和device driver是否自动执行probe的操作,看一下这个attribute的创建过程。

2.1 /sys/bus/i2c/drivers_autoprobe sysfs node创建流程
bus_register(struct bus_type *bus) //msm_kernel\drivers\base\bus.c
struct subsys_private *priv;
priv->subsys.kobj.ktype = &bus_ktype; //bus_ktype
----add_probe_files(bus);
--------bus_create_file(bus, &bus_attr_drivers_autoprobe);
------------sysfs_create_file(&dev->kobj, &attr->attr);
----------------sysfs_create_file_ns(kobj, attr, NULL);
--------------------sysfs_add_file_mode_ns(kobj->sd, attr, false, attr->mode, uid, gid, ns);
------------------------kn = __kernfs_create_file(parent, attr->name, mode & 0777, uid, gid, size, ops, (void *)attr, ns, key);
----------------------------kn->priv = priv; //(void *)attr
----------------------------kernfs_add_one(kn);
--------------------------------kernfs_link_sibling(kn);

关键代码解析:

int sysfs_add_file_mode_ns(struct kernfs_node *parent,
               const struct attribute *attr, bool is_bin,
               umode_t mode, kuid_t uid, kgid_t gid, const void *ns)
{
    struct lock_class_key *key = NULL;
    const struct kernfs_ops *ops;
    struct kernfs_node *kn;
    loff_t size;

    if (!is_bin) {
        struct kobject *kobj = parent->priv;
        const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops;

        /* every kobject with an attribute needs a ktype assigned */
        //如果从属的kobject(就是attribute文件所在的目录)没有ktype,或者没有ktype->sysfs_ops指针,是不允许它注册任何attribute的
        if (WARN(!sysfs_ops, KERN_ERR
             "missing sysfs attribute operations for kobject: %s\n",
             kobject_name(kobj)))
            return -EINVAL;

        if (sysfs_ops->show && sysfs_ops->store) {
            if (mode & SYSFS_PREALLOC)
                ops = &sysfs_prealloc_kfops_rw; //判断ktype->sysfs_ops中的操作函数,确定kernfs_ops ops支持哪些操作,只读/只写/读写
            else
                ops = &sysfs_file_kfops_rw;
        } else if (sysfs_ops->show) {
            if (mode & SYSFS_PREALLOC)
                ops = &sysfs_prealloc_kfops_ro;
            else
                ops = &sysfs_file_kfops_ro;
        } else if (sysfs_ops->store) {
            if (mode & SYSFS_PREALLOC)
                ops = &sysfs_prealloc_kfops_wo;
            else
                ops = &sysfs_file_kfops_wo;
        } else
            ops = &sysfs_file_kfops_empty;

        size = PAGE_SIZE;
    } else {
        struct bin_attribute *battr = (void *)attr;

        if (battr->mmap)
            ops = &sysfs_bin_kfops_mmap;
        else if (battr->read && battr->write)
            ops = &sysfs_bin_kfops_rw;
        else if (battr->read)
            ops = &sysfs_bin_kfops_ro;
        else if (battr->write)
            ops = &sysfs_bin_kfops_wo;
        else
            ops = &sysfs_file_kfops_empty;

        size = battr->size;
    }

#ifdef CONFIG_DEBUG_LOCK_ALLOC
    if (!attr->ignore_lockdep)
        key = attr->key ?: (struct lock_class_key *)&attr->skey;
#endif

    kn = __kernfs_create_file(parent, attr->name, mode & 0777, uid, gid,
                  size, ops, (void *)attr, ns, key);
    if (IS_ERR(kn)) {
        if (PTR_ERR(kn) == -EEXIST)
            sysfs_warn_dup(parent, attr->name);
        return PTR_ERR(kn);
    }
    return 0;
}

/**
* __kernfs_create_file - kernfs internal function to create a file
* @parent: directory to create the file in
* @name: name of the file
* @mode: mode of the file
* @uid: uid of the file
* @gid: gid of the file
* @size: size of the file
* @ops: kernfs operations for the file
* @priv: private data for the file
* @ns: optional namespace tag of the file
* @key: lockdep key for the file's active_ref, %NULL to disable lockdep
*
* Returns the created node on success, ERR_PTR() value on error.
*/
struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
                     const char *name,
                     umode_t mode, kuid_t uid, kgid_t gid,
                     loff_t size,
                     const struct kernfs_ops *ops,
                     void *priv, const void *ns,
                     struct lock_class_key *key)
{
    struct kernfs_node *kn;
    unsigned flags;
    int rc;

    flags = KERNFS_FILE;

    kn = kernfs_new_node(parent, name, (mode & S_IALLUGO) | S_IFREG,
                 uid, gid, flags);
    if (!kn)
        return ERR_PTR(-ENOMEM);

    kn->attr.ops = ops; // kernfs_node ops操作函数初始化 #1
    kn->attr.size = size;
    kn->ns = ns;
    kn->priv = priv; //kernfs_node私有数据赋值,这里是struct attribute *attr,后续读写attribute的时候会用到

#ifdef CONFIG_DEBUG_LOCK_ALLOC
    if (key) {
        lockdep_init_map(&kn->dep_map, "kn->active", key, 0);
        kn->flags |= KERNFS_LOCKDEP;
    }
#endif

    /*
     * kn->attr.ops is accesible only while holding active ref.  We
     * need to know whether some ops are implemented outside active
     * ref.  Cache their existence in flags.
     */
    if (ops->seq_show)
        kn->flags |= KERNFS_HAS_SEQ_SHOW;
    if (ops->mmap)
        kn->flags |= KERNFS_HAS_MMAP;
    if (ops->release)
        kn->flags |= KERNFS_HAS_RELEASE;

    rc = kernfs_add_one(kn);
    if (rc) {
        kernfs_put(kn);
        return ERR_PTR(rc);
    }
    return kn;
}
2.2 bus_ktype

如果从属的kobject(就是attribute文件所在的目录)没有ktype,或者没有ktype->sysfs_ops指针,是不允许它注册任何attribute的。

drivers/base/bus.c:
static struct kobj_type bus_ktype = {
    .sysfs_ops    = &bus_sysfs_ops,
    .release    = bus_release,
};

static const struct sysfs_ops bus_sysfs_ops = {
    .show    = bus_attr_show, // #2
    .store    = bus_attr_store,
};

#define to_bus_attr(_attr) container_of(_attr, struct bus_attribute, attr)
static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,
                 char *buf)
{
    struct bus_attribute *bus_attr = to_bus_attr(attr);
    struct subsys_private *subsys_priv = to_subsys_private(kobj);
    ssize_t ret = 0;

    if (bus_attr->show)
        ret = bus_attr->show(subsys_priv->bus, buf); // #3
    return ret;
}

static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,
                  const char *buf, size_t count)
{
    struct bus_attribute *bus_attr = to_bus_attr(attr);
    struct subsys_private *subsys_priv = to_subsys_private(kobj);
    ssize_t ret = 0;

    if (bus_attr->store)
        ret = bus_attr->store(subsys_priv->bus, buf, count);
    return ret;
}
2.3 bus_attribute
struct bus_attribute {
    struct attribute    attr;
    ssize_t (*show)(struct bus_type *bus, char *buf);
    ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
};

static ssize_t drivers_autoprobe_show(struct bus_type *bus, char *buf)
{
    return sysfs_emit(buf, "%d\n", bus->p->drivers_autoprobe);
}

static ssize_t drivers_autoprobe_store(struct bus_type *bus,
                       const char *buf, size_t count)
{
    if (buf[0] == '0')
        bus->p->drivers_autoprobe = 0;
    else
        bus->p->drivers_autoprobe = 1;
    return count;
}

static BUS_ATTR_RW(drivers_autoprobe);

ATTR 宏展开:

#define BUS_ATTR_RW(_name) \
    struct bus_attribute bus_attr_##_name = __ATTR_RW(_name)
#define BUS_ATTR_RO(_name) \
    struct bus_attribute bus_attr_##_name = __ATTR_RO(_name)
#define BUS_ATTR_WO(_name) \
    struct bus_attribute bus_attr_##_name = __ATTR_WO(_name)
    

#define __ATTR(_name, _mode, _show, _store) {                \
    .attr = {.name = __stringify(_name),                \
         .mode = VERIFY_OCTAL_PERMISSIONS(_mode) },        \
    .show    = _show,                        \
    .store    = _store,                        \
}

#define __ATTR_PREALLOC(_name, _mode, _show, _store) {            \
    .attr = {.name = __stringify(_name),                \
         .mode = SYSFS_PREALLOC | VERIFY_OCTAL_PERMISSIONS(_mode) },\
    .show    = _show,                        \
    .store    = _store,                        \
}

#define __ATTR_RO(_name) {                        \
    .attr    = { .name = __stringify(_name), .mode = 0444 },        \
    .show    = _name##_show,                        \
}

#define __ATTR_RO_MODE(_name, _mode) {                    \
    .attr    = { .name = __stringify(_name),                \
            .mode = VERIFY_OCTAL_PERMISSIONS(_mode) },        \
    .show    = _name##_show,                        \
}

#define __ATTR_RW_MODE(_name, _mode) {                    \
    .attr    = { .name = __stringify(_name),                \
            .mode = VERIFY_OCTAL_PERMISSIONS(_mode) },        \
    .show    = _name##_show,                        \
    .store    = _name##_store,                    \
}

#define __ATTR_WO(_name) {                        \
    .attr    = { .name = __stringify(_name), .mode = 0200 },        \
    .store    = _name##_store,                    \
}

#define __ATTR_RW(_name) __ATTR(_name, 0644, _name##_show, _name##_store)

3,应用程序read sysfs属性文件是如何调用到属性文件的show函数的

3.1 cat sysfs节点流程
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) //fs/read_write.c
----ksys_read(fd, buf, count);
--------vfs_read(f.file, buf, count, ppos);
------------.read_iter    = kernfs_fop_read_iter //fs/kernfs/file.c
----------------seq_read_iter(iocb, iter);
--------------------struct seq_file *m = iocb->ki_filp->private_data;
--------------------p = m->op->start(m, &m->index);
--------------------m->op->show(m, p);
------------------------.show = kernfs_seq_show
----------------------------of->kn->attr.ops->seq_show(sf, v);
--------------------------------.seq_show    = sysfs_kf_seq_show,
------------------------------------const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
----------------------------------------ops->show(kobj, of->kn->priv, buf);
--------------------------------------------.show    = bus_attr_show, //msm_kernel\drivers\base\bus.c
------------------------------------------------struct bus_attribute *bus_attr = to_bus_attr(attr);
------------------------------------------------bus_attr->show(subsys_priv->bus, buf);
----------------------------------------------------drivers_autoprobe_show(struct bus_type *bus, char *buf)

msm_kernel\fs\kernfs\file.c
const struct file_operations kernfs_file_fops = {
    .read_iter    = kernfs_fop_read_iter,
    .write_iter    = kernfs_fop_write_iter,
    .llseek        = generic_file_llseek,
    .mmap        = kernfs_fop_mmap,
    .open        = kernfs_fop_open,
    .release    = kernfs_fop_release,
    .poll        = kernfs_fop_poll,
    .fsync        = noop_fsync,
    .splice_read    = generic_file_splice_read,
    .splice_write    = iter_file_splice_write,
};

msm_kernel\fs\kernfs\file.c
static const struct seq_operations kernfs_seq_ops = {
    .start = kernfs_seq_start,
    .next = kernfs_seq_next,
    .stop = kernfs_seq_stop,
    .show = kernfs_seq_show,
};

msm_kernel\fs\sysfs\file.c
static const struct kernfs_ops sysfs_file_kfops_ro = {
    .seq_show    = sysfs_kf_seq_show,
};
3.2 dump log
eg:
cat /sys/bus/i2c/drivers_autoprobe

10-18 05:40:09.284  8993  8993 I Call trace:  
10-18 05:40:09.284  8993  8993 I         : dump_backtrace.cfi_jt+0x0/0x8
10-18 05:40:09.284  8993  8993 I         : dump_stack_lvl+0xe4/0x180
10-18 05:40:09.284  8993  8993 I         : drivers_autoprobe_show+0x28/0x5c
10-18 05:40:09.285  8993  8993 I         : bus_attr_show+0x40/0x8c
10-18 05:40:09.285  8993  8993 I         : sysfs_kf_seq_show+0xbc/0x158
10-18 05:40:09.285  8993  8993 I         : kernfs_seq_show+0x54/0x9c
10-18 05:40:09.285  8993  8993 I         : seq_read_iter+0x174/0x698
10-18 05:40:09.285  8993  8993 I         : kernfs_fop_read_iter+0x70/0x2dc
10-18 05:40:09.285  8993  8993 I         : vfs_read+0x2ec/0x368
10-18 05:40:09.285  8993  8993 I         : ksys_read+0x7c/0xf0
10-18 05:40:09.285  8993  8993 I         : __arm64_sys_read+0x20/0x30
10-18 05:40:09.285  8993  8993 I         : el0_svc_common+0xd8/0x27c
10-18 05:40:09.285  8993  8993 I         : el0_svc+0x38/0x9c
10-18 05:40:09.285  8993  8993 I         : el0_sync_handler+0x8c/0xf0
10-18 05:40:09.285  8993  8993 I         : el0_sync+0x1b4/0x1c0
3.3 关键代码解析

1)当user space read drivers_autoprobe节点的时候,会触发系统调用

SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{
    return ksys_read(fd, buf, count);
}

然后会由VFS转到 file_operations 的read操作。

2)在kernfs_fop_open函数中,initialize sequential file

static int kernfs_fop_open(struct inode *inode, struct file *file)
{
    struct kernfs_node *kn = inode->i_private;
    struct kernfs_root *root = kernfs_root(kn);
    const struct kernfs_ops *ops;
    struct kernfs_open_file *of;
    bool has_read, has_write, has_mmap;
    int error = -EACCES;

    if (!kernfs_get_active(kn))
        return -ENODEV;

    ops = kernfs_ops(kn);

    has_read = ops->seq_show || ops->read || ops->mmap;
    has_write = ops->write || ops->mmap;
    has_mmap = ops->mmap;

    /* see the flag definition for details */
    if (root->flags & KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK) {
        if ((file->f_mode & FMODE_WRITE) &&
            (!(inode->i_mode & S_IWUGO) || !has_write))
            goto err_out;


        if ((file->f_mode & FMODE_READ) &&
            (!(inode->i_mode & S_IRUGO) || !has_read))
            goto err_out;
    }

        ... ...

    /*
     * Always instantiate seq_file even if read access doesn't use
     * seq_file or is not requested.  This unifies private data access
     * and readable regular files are the vast majority anyway.
     */
    if (ops->seq_show)
        error = seq_open(file, &kernfs_seq_ops); //initialize sequential file, sets @file, associating it with a sequence described by @op
    else
        error = seq_open(file, NULL);
    if (error)
        goto err_free;

    of->seq_file = file->private_data;
    of->seq_file->private = of;

    /* seq_file clears PWRITE unconditionally, restore it if WRITE */
    if (file->f_mode & FMODE_WRITE)
        file->f_mode |= FMODE_PWRITE;
        ... ...
}

3)

static int kernfs_seq_show(struct seq_file *sf, void *v)
{
    struct kernfs_open_file *of = sf->private;

    of->event = atomic_read(&of->kn->attr.open->event);

    return of->kn->attr.ops->seq_show(sf, v); //在attribute节点创建的过程中赋值,上文#1的位置
}

4)可供选择的kernfs_ops

static const struct kernfs_ops sysfs_file_kfops_empty = {
};

static const struct kernfs_ops sysfs_file_kfops_ro = {
    .seq_show    = sysfs_kf_seq_show,
};

static const struct kernfs_ops sysfs_file_kfops_wo = {
    .write        = sysfs_kf_write,
};

static const struct kernfs_ops sysfs_file_kfops_rw = {
    .seq_show    = sysfs_kf_seq_show,
    .write        = sysfs_kf_write,
};

static const struct kernfs_ops sysfs_prealloc_kfops_ro = {
    .read        = sysfs_kf_read,
    .prealloc    = true,
};

static const struct kernfs_ops sysfs_prealloc_kfops_wo = {
    .write        = sysfs_kf_write,
    .prealloc    = true,
};

static const struct kernfs_ops sysfs_prealloc_kfops_rw = {
    .read        = sysfs_kf_read,
    .write        = sysfs_kf_write,
    .prealloc    = true,
};

5)找到 sysfs_ops

static int sysfs_kf_seq_show(struct seq_file *sf, void *v)
{
    struct kernfs_open_file *of = sf->private;
    struct kobject *kobj = of->kn->parent->priv;
    const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
    ssize_t count;
    char *buf;

    /* acquire buffer and ensure that it's >= PAGE_SIZE and clear */
    count = seq_get_buf(sf, &buf);
    if (count < PAGE_SIZE) {
        seq_commit(sf, -1);
        return 0;
    }
    memset(buf, 0, PAGE_SIZE);

    /*
     * Invoke show().  Control may reach here via seq file lseek even
     * if @ops->show() isn't implemented.
     */
    if (ops->show) {
        count = ops->show(kobj, of->kn->priv, buf); //上文#2处定义了show函数
        if (count < 0)
            return count;
    }

kobj->ktype->sysfs_ops :

/*
* Determine ktype->sysfs_ops for the given kernfs_node.  This function
* must be called while holding an active reference.
*/
static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn)
{
    struct kobject *kobj = kn->parent->priv;

    if (kn->flags & KERNFS_LOCKDEP)
        lockdep_assert_held(kn);
    return kobj->ktype ? kobj->ktype->sysfs_ops : NULL;
}

6)

终于在上文#3处调用到了drivers_autoprobe的show函数。

4,bus_attribute/device_attribute/driver_attribute

所有需要使用attribute的模块,都不会直接定义struct attribute变量,而是通过一个自定义的数据结构,该数据结构的一个成员是struct attribute类型的变量,并提供show和store回调函数。然后在该模块ktype所对应的struct sysfs_ops变量中,实现该本模块整体的show和store函数,并在被调用时,转接到自定义数据结构(struct class_attribute)中的show和store函数中。这样,每个atrribute文件,实际上对应到一个自定义数据结构变量中了。

4.1 bus_attribute
struct bus_attribute {
    struct attribute    attr;
    ssize_t (*show)(struct bus_type *bus, char *buf);
    ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
};
4.2 device_attribute
/* interface for exporting device attributes */
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);
};

//device_attribute 节点创建举例
static ssize_t map_seg7_show(struct device *dev,
                 struct device_attribute *attr,
                 char *buf)
{
    memcpy(buf, &txx9_seg7map, sizeof(txx9_seg7map));
    return sizeof(txx9_seg7map);
}

static ssize_t map_seg7_store(struct device *dev,
                  struct device_attribute *attr,
                  const char *buf, size_t size)
{
    if (size != sizeof(txx9_seg7map))
        return -EINVAL;
    memcpy(&txx9_seg7map, buf, size);
    return size;
}
static DEVICE_ATTR(map_seg7, 0600, map_seg7_show, map_seg7_store);

error = device_create_file(tx_7segled_subsys.dev_root, &dev_attr_map_seg7);
4.3 driver_attribute
/* sysfs interface for exporting driver attributes */
struct driver_attribute {
    struct attribute attr;
    ssize_t (*show)(struct device_driver *driver, char *buf);
    ssize_t (*store)(struct device_driver *driver, const char *buf,
             size_t count);
};

//driver_attribute 节点创建举例
static ssize_t new_id_show(struct device_driver *driver, char *buf)
{
    struct usb_driver *usb_drv = to_usb_driver(driver);

    return usb_show_dynids(&usb_drv->dynids, buf);
}

static ssize_t new_id_store(struct device_driver *driver,
                const char *buf, size_t count)
{
    struct usb_driver *usb_drv = to_usb_driver(driver);

    return usb_store_new_id(&usb_drv->dynids, usb_drv->id_table, driver, buf, count);
}
static DRIVER_ATTR_RW(new_id);

error = driver_create_file(&usb_drv->drvwrap.driver,
        &driver_attr_new_id);

参考链接:

Linux kernel sysfs device_attribute节点的创建和读写分析_如何在kernel中读取节点-CSDN博客

Linux设备模型(4)_sysfs

  • 16
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值