sysfs_create_group的简单使用

发布于:2025-04-02 ⋅ 阅读:(22) ⋅ 点赞:(0)

概述

在 Linux 驱动开发中,device_create_file() 是用于为设备创建单个属性文件的函数。如果你需要为一个设备创建多个属性文件(即多个 sysfs 属性),可以通过多次调用 device_create_file() 来实现。

不过,为了提高代码的可读性和维护性,Linux 内核提供了一种更高效的方式——使用 sysfs_create_group()机制来批量定义和注册多个属性文件。

设备树示例

包含兼容属性,pinctrl的名字和配置,gpio属性

eta: eta {
		compatible = "ww,eta4056";
		pinctrl-names = "default";
		pinctrl-0 = <&eta_pmx_func>;
		stat-gpio = <49>;
		en-gpio = <50>;
		status = "okay";
	};

代码示例

包括对pinctrl的获取和设置;gpio的读取;gpio的输入输出配置;attr的配置

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/device.h>
#include <linux/sysfs.h>

static int status_gpio;
static int en_gpio;

ssize_t status_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	int value = gpio_get_value(status_gpio);
	return sprintf(buf, "%d\n", value);
}

ssize_t en_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
	gpio_set_value(en_gpio, buf[0]-'0');
	return count;
}

ssize_t volt_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	return sprintf(buf, "TODO\n");
}

ssize_t temp_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	return sprintf(buf, "TODO\n");
}

static DEVICE_ATTR_RO(status);
static DEVICE_ATTR_WO(en);
static DEVICE_ATTR_RO(volt);
static DEVICE_ATTR_RO(temp);

static struct attribute *arry_attrs[] = {
    &dev_attr_status.attr,
    &dev_attr_en.attr,
    &dev_attr_volt.attr,
    &dev_attr_temp.attr,
    NULL, 
};

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

static int eta4056_probe(struct platform_device *pdev) {

    struct pinctrl *pinctrl;
    struct device *dev = &pdev->dev;
    struct device_node *node = dev->of_node;

    printk("eta4056_init\n");
    
    pinctrl = devm_pinctrl_get_select_default(dev);
	if (IS_ERR(pinctrl))
        	dev_warn(dev, "pins are not configured\n");

    of_property_read_u32(node, "stat-gpio", &status_gpio );
	if (status_gpio < 0)
		return -ENODEV;
    
    gpio_request_one(status_gpio, GPIOF_DIR_IN, "eta_status");

    of_property_read_u32(node, "en-gpio", &en_gpio );
	if (en_gpio < 0)
		return -ENODEV;
    
    gpio_request_one(en_gpio, GPIOF_OUT_INIT_HIGH, "eta_en");

    if (sysfs_create_group(&pdev->dev.kobj, &attr_group)) {
        pr_err("Failed to create attribute group\n");
        return -ENODEV;
    }

    return 0;
}

static int eta4056_exit(struct platform_device *pdev) {
    
    printk("eta4056_exit\n");
    return 0 ;
}

static const struct of_device_id eta_ids[] = {
	{ .compatible = "ww,eta4056", .data = NULL },
	{},
};

static struct platform_driver eta4056_driver = {
	.probe = eta4056_probe,
	.remove = eta4056_exit,
	.driver = {
		.name = "eta4056",
        .of_match_table = eta_ids,
	},
};

static int __init eta4056_init(void)
{
	return platform_driver_probe(&eta4056_driver, eta4056_probe);
}
device_initcall_sync(eta4056_init);

MODULE_DESCRIPTION("eta4056 Driver");
MODULE_LICENSE("GPL v2");

最终呈现如下

代码解析

sysfs_create_group调用internal_create_group来完成多个属性文件的创建

int sysfs_create_group(struct kobject *kobj,
		       const struct attribute_group *grp)
{
	return internal_create_group(kobj, 0, grp);
}
EXPORT_SYMBOL_GPL(sysfs_create_group);

internal_create_group 增加 kernfs_node 的引用计数,确保在后续操作中不会被意外释放,create_files() 是核心函数,负责根据 grp->attrs 和 grp->bin_attrs 创建对应的 sysfs 文件

 

static int internal_create_group(struct kobject *kobj, int update,
				 const struct attribute_group *grp)
{
	struct kernfs_node *kn;
	kuid_t uid;
	kgid_t gid;
	int error;

	if (WARN_ON(!kobj || (!update && !kobj->sd)))
		return -EINVAL;

	/* Updates may happen before the object has been instantiated */
	if (unlikely(update && !kobj->sd))
		return -EINVAL;
	if (!grp->attrs && !grp->bin_attrs) {
		WARN(1, "sysfs: (bin_)attrs not set by subsystem for group: %s/%s\n",
			kobj->name, grp->name ?: "");
		return -EINVAL;
	}
	kobject_get_ownership(kobj, &uid, &gid);
	if (grp->name) {
		if (update) {
			kn = kernfs_find_and_get(kobj->sd, grp->name);
			if (!kn) {
				pr_warn("Can't update unknown attr grp name: %s/%s\n",
					kobj->name, grp->name);
				return -EINVAL;
			}
		} else {
			kn = kernfs_create_dir_ns(kobj->sd, grp->name,
						  S_IRWXU | S_IRUGO | S_IXUGO,
						  uid, gid, kobj, NULL);
			if (IS_ERR(kn)) {
				if (PTR_ERR(kn) == -EEXIST)
					sysfs_warn_dup(kobj->sd, grp->name);
				return PTR_ERR(kn);
			}
		}
	} else
		kn = kobj->sd;
	kernfs_get(kn);
	error = create_files(kn, kobj, uid, gid, grp, update);
	if (error) {
		if (grp->name)
			kernfs_remove(kn);
	}
	kernfs_put(kn);

	if (grp->name && update)
		kernfs_put(kn);

	return error;
}


网站公告

今日签到

点亮在社区的每一天
去签到