第12章工程中的 Linux设备驱动之基于 sysfs 的设备驱动

12.6 基于 sysfs (sys文件系统)的设备驱动

    一些设备驱动以 sysfs 结点的形式存在,其本身没有对应的/dev 结点;一些设备驱动虽然有对应的/dev 结点,也依赖于sysfs 结点进行一些工作。

    Linux 专门提供了一种类型的设备驱动,以结构体sysdev_driver 进行描述,该结构体的定义如代码清单12.22 所示。

    代码清单 12.22 sysdev_driver 

2.6内核

include/linux/sysdev.h

struct sysdev_driver {
        struct list_head        entry;
        int     (*add)(struct sys_device *);
        int     (*remove)(struct sys_device *);
        int     (*shutdown)(struct sys_device *);
        int     (*suspend)(struct sys_device *, pm_message_t state);
        int     (*resume)(struct sys_device *);
};

注册和注销此类驱动的API 为:

int sysdev_driver_register(struct sysdev_class *, struct sysdev_driver *);

void sysdev_driver_unregister(struct sysdev_class *, struct sysdev_driver *);

而此类驱动中通常会通过如下两个API 来创建和移除sysfs 的结点:

int sysdev_create_file(struct sys_device *, struct sysdev_attribute *);

void sysdev_remove_file(struct sys_device *, struct sysdev_attribute *);

drivers/base/sys.c

int sysdev_create_file(struct sys_device * s, struct sysdev_attribute * a)
{
        return sysfs_create_file(&s->kobj, &a->attr);
}

void sysdev_remove_file(struct sys_device * s, struct sysdev_attribute * a)
{
        sysfs_remove_file(&s->kobj, &a->attr);

}

从2.6.22版本内核源代码看出,sysdev_create_file()最终调用的是sysfs_create_file ()。

include/linux/sysfs.h

      extern int __must_check sysfs_create_file(struct kobject *, const struct attribute *);

      sysfs_create_file ()的第一个参数为kobject 的指针,第二个参数为attribute的指针。

     每个attribute 对应着sysfs 中的一个文件,而读写一个attribute对应的文件通常需要show()和store()这两个函数,形如:

    static ssize_t xxx_show(struct kobject * kobj, struct attribute * attr, char * buffer);
    static ssize_t xxx_store(struct kobject * kobj, struct attribute * attr, const char * buffer, size_t count);

    如CPU 频率驱动cpufreq(位于drivers/cpufreq)就是一个sysdev_driver 形式的驱动,主要工作就是提供一些sysfs的结点,cpuinfo_cur_freq、cpuinfo_max_freq、cpuinfo_min_freq、

scaling_available_frequencies、scaling_available_governors、scaling_cur_freq、scaling_driver、scaling_governor、scaling_max_freq、scaling_min_freq 等。用户空间可以手动 cat、echo 来操作这些结点或者使用cpufrequtils 工具访问这些结点以与内核通信。

参见drivers/cpufreq/cpufreq.c:


#define define_one_ro(_name) \
static struct freq_attr _name = \
__ATTR(_name, 0444, show_##_name, NULL)

#define define_one_ro0400(_name) \
static struct freq_attr _name = \
__ATTR(_name, 0400, show_##_name, NULL)

#define define_one_rw(_name) \
static struct freq_attr _name = \
__ATTR(_name, 0644, show_##_name, store_##_name)

define_one_ro0400(cpuinfo_cur_freq);
define_one_ro(cpuinfo_min_freq);
define_one_ro(cpuinfo_max_freq);
define_one_ro(scaling_available_governors);
define_one_ro(scaling_driver);
define_one_ro(scaling_cur_freq);
define_one_ro(affected_cpus);
define_one_rw(scaling_min_freq);
define_one_rw(scaling_max_freq);
define_one_rw(scaling_governor);

    还有一类设备虽然不以 sysdev_driver 的形式存在,但是其本质上只是包含 sysfs 结点。典型例子包括I 2 C EEPROM(电可擦可编程只读存储器),以I 2 C Client 驱动的形式存在,但该驱动 drivers/i2c/chips/eeprom.c 通过 sysfs_create_bin_file()创建二进制 sysfs 文件,该二进制结点对应的bin_attribute 如代码清单12.23 所示。

代码清单 12.23 EEPROM 驱动的 bin_attribute 实例

static struct bin_attribute eeprom_attr = {
.attr = {
.name = "eeprom",
.mode = S_IRUGO, /* mode */
.owner = THIS_MODULE,
},
.size = EEPROM_SIZE,
.read = eeprom_read,
};

通过/sys 目录里的“eeprom”文件即可访问该EEPROM。创建这个结点的语句是:

/* create the sysfs eeprom file */

err = sysfs_create_bin_file(&new_client->dev.kobj, &eeprom_attr);

      其中第1 个参数是bin_attribute 所对应设备的kobject 指针,这预示着该“eeprom”在/sys 中将位于

client->dev 这个device 的目录之下。

    案例:

在sys目录下创建一个目录test,在test目录创建一个属性文件test,且这个test文件具有只读属性,当用户空间读取这个属性文件时,返回一个字符串。

#include <linux/init.h>
#include <linux/module.h>  
#include <linux/kobject.h>
#include <linux/types.h>

static struct kobject *kobj = NULL;

static ssize_t 
test_sys_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", "sysfs test read");
}

static struct kobj_attribute 
test_sysfs_read = __ATTR(test, 0444, test_sys_show, NULL);

static struct attribute *sysfs_test[] = {
&test_sysfs_read.attr,
NULL,
};

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

static int __init sys_fs_init(void)
{
int ret;

kobj = kobject_create_and_add("test", NULL);
if (!kobj)
goto err_kobject_create_and_add;

ret = sysfs_create_group(kobj, &attr_group);
if (ret)
goto err_sysfs_create_group;

printk(KERN_INFO "----sys_fs_init!----\n");

return 0;

err_sysfs_create_group:
kobject_put(kobj);
sysfs_remove_group(kobj, &attr_group);
printk("sysfs_create_group error: %s\n",__func__);
return 0;
err_kobject_create_and_add:
printk("kobject_create_and_add error: %s\n", __func__);
 return 0;
}

static void __exit sys_fs_exit(void)
{
kobject_put(kobj);
sysfs_remove_group(kobj, &attr_group);
        printk(KERN_INFO "----sys_fs_exit!----\n");
}


module_init(sys_fs_init);
module_exit(sys_fs_exit);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("[email protected]");
MODULE_DESCRIPTION("sysfs demo");


猜你喜欢

转载自blog.csdn.net/xiezhi123456/article/details/80674831
今日推荐