如何将文件操作附加到平台驱动程序中的 sysfs 属性?
How to attach file operations to sysfs attribute in platform driver?
我为我们开发的外围设备编写了一个平台驱动程序,并希望向 sysfs 公开一些配置选项。我已经设法在探测函数中使用属性结构(见下文)和 sysfs_create_file
创建了适当的文件,但我无法弄清楚如何将 show/store 函数附加到平台驱动程序中的结构.
我在网上找到的大多数资源都使用 device_attribute
结构或类似的东西来创建它们的文件,这在这里也合适吗?对于平台驱动程序,还有其他方法可以做到这一点吗?
我的属性结构如下所示:
struct attribute subkey_attr = {
.name = "subkeys",
.mode = S_IWUGO | S_IRUGO,
};
然后我使用此调用注册文件:
riddler_kobject = &pdev->dev.kobj;
ret_val = sysfs_create_file(riddler_kobject, &subkey_attr);
归结为下一个:
- 重新使用来自
struct device
(来自您的 struct platform_device
)的现有 kobject 用于 sysfs_create_group()
(而不是创建您自己的 kobject
)
- 使用
DEVICE_ATTR()
声明 struct device_attribute
而不是常规的 __ATTR()
,这会创建 struct kobj_attribute
.
下面是我如何为我的平台驱动程序创建 sysfs 属性。
为您的 sysfs 属性(文件)创建您将在 show()
/ store()
操作中用作私有数据的结构。例如:
struct mydrv {
struct device *dev;
long myparam;
};
在你的驱动程序中分配这个结构 probe()
:
static int mydrv_probe(struct platform_device *pdev)
{
struct mydrv *mydrv;
mydrv = devm_kzalloc(&pdev->dev, sizeof(*mydrv), GFP_KERNEL);
mydrv->dev = &pdev->dev;
platform_set_drvdata(pdev, mydrv);
...
}
创建 show()
/ store()
函数:
static ssize_t mydrv_myparam_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mydrv *mydrv = dev_get_drvdata(dev);
int len;
len = sprintf(buf, "%d\n", mydrv->myparam);
if (len <= 0)
dev_err(dev, "mydrv: Invalid sprintf len: %d\n", len);
return len;
}
static ssize_t mydrv_myparam_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mydrv *mydrv = dev_get_drvdata(dev);
kstrtol(buf, 10, &mydrv->myparam);
return count;
}
为这些函数创建设备属性(在这些函数之后):
static DEVICE_ATTR(myparam, S_IRUGO | S_IWUSR, mydrv_myparam_show,
mydrv_myparam_store);
声明属性table(实际上为您的驱动程序列出 sysfs 文件):
static struct attribute *mydrv_attrs[] = {
&dev_attr_myparam.attr,
NULL
};
声明属性组(实际上为您的驱动程序指定 sysfs 目录):
static struct attribute_group mydrv_group = {
.name = "mydrv",
.attrs = mydrv_attrs,
};
static struct attribute_group *mydrv_groups[] = {
&mydrv_group,
NULL
}
实际上可以用一行代替:
ATTRIBUTE_GROUPS(mydrv);
在驱动程序的 probe()
函数中创建 sysfs 目录和文件:
static int mydrv_probe(struct platform_device *pdev)
{
int ret;
...
ret = sysfs_create_group(&pdev->dev.kobj, &mydrv_group);
if (ret) {
dev_err(&pdev->dev, "sysfs creation failed\n");
return ret;
}
...
}
在驱动程序的 remove()
函数中删除您的 sysfs 文件:
static int mydrv_remove(struct platform_device *pdev)
{
sysfs_remove_group(&pdev->dev.kobj, &mydrv_group);
...
}
竞争条件注释
正如@FranzForstmayr 正确指出的那样,在 mydrv_probe()
中添加具有 sysfs_create_group()
的 sysfs 文件时,可能存在 竞争条件 。这是因为 user-space 可以在调用 mydrv_probe()
之前就已经知道这些文件存在(这些文件实际上是由 sysfs_create_group()
函数创建的)。 Greg Kroah-Hartman 的 "How to Create a sysfs File Correctly" 文章详细介绍了这个问题。
因此在我们的 platform_device
案例中,您可以使用 默认属性组 [=133=,而不是调用 sysfs_create_group()
(及其对应的 sysfs_remove_group()
) ].为此,您需要将 struct device
的相应 .groups
字段分配给属性组变量:
static int mydrv_probe(struct platform_device *pdev)
{
...
pdev->dev.groups = mydrv_groups;
...
}
免责声明:我没有测试这段代码,虽然它应该可以工作,因为 this 代码。
请参阅 [1,2,3] 链接以获取有关上述竞争条件的更多见解。
更多示例,运行内核源目录中的下一个命令:
$ git grep -l --all-match -e platform_device -e attribute -e '\.groups =' -- drivers/
您也可以在提交消息中通过 "default attribute" 搜索:
$ git log --no-merges --oneline --grep="default attribute" -- drivers/
我通过这种方式找到的一些提交:[4,5,6,7]。
参考资料
[1]
[2] PATCH: sysfs: add devm_sysfs_create_group() and friends
[3] [GIT PATCH] Driver core patches for 3.11-rc2
[4]commit 1
[5]commit 2
[6]commit 3
[7]commit 4
没有足够的声誉 post 发表评论,但我只想评论接受的答案中的 默认属性组 注释。
我的理解是,这不应该添加到探测函数中,如示例中所示,而是应该在设备结构中设置(或 device_driver、class 或总线,具体取决于您的驱动程序) 通常在文件末尾定义。
例如:
static struct device iio_evgen_dev = {
.bus = &iio_bus_type,
.groups = iio_evgen_groups,
.release = &iio_evgen_release,
};
来自 this 示例
奇怪的是,根据 this 使用 DEVICE_INT_ATTR
创建属性时无法正常工作,所以不确定这是怎么回事。
此外,我不是 100% 确定,但我认为 this 是在加载驱动程序时调用的,而不是在探测设备时调用的。
这是对 Sam Protsenko 和 Anthony 的回答的补充
如果您通过 DEVICE_ATTR
宏创建 设备 属性,则必须在 .dev_groups
中注册属性组 (mydrv_groups
)而不是 .groups
字段。
static struct device iio_evgen_dev = {
.bus = &iio_bus_type,
.dev_groups = iio_evgen_groups, // .dev_groups for DEVICE_ATTR
.groups = another_attr_group, // .groups for DRIVER_ATTR
.release = &iio_evgen_release,
};
然后属性会自动正确注册,而无需在 probe/remove 函数中设置任何内容,如 Greg Kroah-Hartman 的文章所述。
假设模块已经加载到内核中并且驱动程序注册在
/sys/bus/platform/drivers/mydrv
每个 设备 实例都将是驱动程序文件夹的子目录,如
/sys/bus/platform/drivers/mydrv/mydrv1
/sys/bus/platform/drivers/mydrv/mydrv2
在 .groups
字段中注册属性会在 driver 文件夹中创建属性。
在 .dev_groups
字段中注册属性会在 设备 的实例文件夹中创建属性。
mydrv
├── driver_attr1
├── driver_attr2
└── mydrv1
├── device_attr1
└── device_attr2
.groups
字段中属性的 show/store 函数无法通过 platform_set_drvdata(pdev, mydrv)
访问驱动程序数据集。
至少不是通过 dev_get_drvdata(dev)
访问它。
通过 dev_get_drvdata(dev)
returns NULL
访问驱动程序数据并取消引用它会导致内核 oops。
反过来,.dev_groups
字段中属性的 show/store 函数可以通过
访问驱动程序数据
struct mydrv *mydrv = dev_get_drvdata(dev);
我为我们开发的外围设备编写了一个平台驱动程序,并希望向 sysfs 公开一些配置选项。我已经设法在探测函数中使用属性结构(见下文)和 sysfs_create_file
创建了适当的文件,但我无法弄清楚如何将 show/store 函数附加到平台驱动程序中的结构.
我在网上找到的大多数资源都使用 device_attribute
结构或类似的东西来创建它们的文件,这在这里也合适吗?对于平台驱动程序,还有其他方法可以做到这一点吗?
我的属性结构如下所示:
struct attribute subkey_attr = {
.name = "subkeys",
.mode = S_IWUGO | S_IRUGO,
};
然后我使用此调用注册文件:
riddler_kobject = &pdev->dev.kobj;
ret_val = sysfs_create_file(riddler_kobject, &subkey_attr);
归结为下一个:
- 重新使用来自
struct device
(来自您的struct platform_device
)的现有 kobject 用于sysfs_create_group()
(而不是创建您自己的kobject
) - 使用
DEVICE_ATTR()
声明struct device_attribute
而不是常规的__ATTR()
,这会创建struct kobj_attribute
.
下面是我如何为我的平台驱动程序创建 sysfs 属性。
为您的 sysfs 属性(文件)创建您将在
show()
/store()
操作中用作私有数据的结构。例如:struct mydrv { struct device *dev; long myparam; };
在你的驱动程序中分配这个结构
probe()
:static int mydrv_probe(struct platform_device *pdev) { struct mydrv *mydrv; mydrv = devm_kzalloc(&pdev->dev, sizeof(*mydrv), GFP_KERNEL); mydrv->dev = &pdev->dev; platform_set_drvdata(pdev, mydrv); ... }
创建
show()
/store()
函数:static ssize_t mydrv_myparam_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mydrv *mydrv = dev_get_drvdata(dev); int len; len = sprintf(buf, "%d\n", mydrv->myparam); if (len <= 0) dev_err(dev, "mydrv: Invalid sprintf len: %d\n", len); return len; } static ssize_t mydrv_myparam_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mydrv *mydrv = dev_get_drvdata(dev); kstrtol(buf, 10, &mydrv->myparam); return count; }
为这些函数创建设备属性(在这些函数之后):
static DEVICE_ATTR(myparam, S_IRUGO | S_IWUSR, mydrv_myparam_show, mydrv_myparam_store);
声明属性table(实际上为您的驱动程序列出 sysfs 文件):
static struct attribute *mydrv_attrs[] = { &dev_attr_myparam.attr, NULL };
声明属性组(实际上为您的驱动程序指定 sysfs 目录):
static struct attribute_group mydrv_group = { .name = "mydrv", .attrs = mydrv_attrs, }; static struct attribute_group *mydrv_groups[] = { &mydrv_group, NULL }
实际上可以用一行代替:
ATTRIBUTE_GROUPS(mydrv);
在驱动程序的
probe()
函数中创建 sysfs 目录和文件:static int mydrv_probe(struct platform_device *pdev) { int ret; ... ret = sysfs_create_group(&pdev->dev.kobj, &mydrv_group); if (ret) { dev_err(&pdev->dev, "sysfs creation failed\n"); return ret; } ... }
在驱动程序的
remove()
函数中删除您的 sysfs 文件:static int mydrv_remove(struct platform_device *pdev) { sysfs_remove_group(&pdev->dev.kobj, &mydrv_group); ... }
竞争条件注释
正如@FranzForstmayr 正确指出的那样,在 mydrv_probe()
中添加具有 sysfs_create_group()
的 sysfs 文件时,可能存在 竞争条件 。这是因为 user-space 可以在调用 mydrv_probe()
之前就已经知道这些文件存在(这些文件实际上是由 sysfs_create_group()
函数创建的)。 Greg Kroah-Hartman 的 "How to Create a sysfs File Correctly" 文章详细介绍了这个问题。
因此在我们的 platform_device
案例中,您可以使用 默认属性组 [=133=,而不是调用 sysfs_create_group()
(及其对应的 sysfs_remove_group()
) ].为此,您需要将 struct device
的相应 .groups
字段分配给属性组变量:
static int mydrv_probe(struct platform_device *pdev)
{
...
pdev->dev.groups = mydrv_groups;
...
}
免责声明:我没有测试这段代码,虽然它应该可以工作,因为 this 代码。
请参阅 [1,2,3] 链接以获取有关上述竞争条件的更多见解。
更多示例,运行内核源目录中的下一个命令:
$ git grep -l --all-match -e platform_device -e attribute -e '\.groups =' -- drivers/
您也可以在提交消息中通过 "default attribute" 搜索:
$ git log --no-merges --oneline --grep="default attribute" -- drivers/
我通过这种方式找到的一些提交:[4,5,6,7]。
参考资料
[1]
[2] PATCH: sysfs: add devm_sysfs_create_group() and friends
[3] [GIT PATCH] Driver core patches for 3.11-rc2
[4]commit 1
[5]commit 2
[6]commit 3
[7]commit 4
没有足够的声誉 post 发表评论,但我只想评论接受的答案中的 默认属性组 注释。 我的理解是,这不应该添加到探测函数中,如示例中所示,而是应该在设备结构中设置(或 device_driver、class 或总线,具体取决于您的驱动程序) 通常在文件末尾定义。 例如:
static struct device iio_evgen_dev = {
.bus = &iio_bus_type,
.groups = iio_evgen_groups,
.release = &iio_evgen_release,
};
来自 this 示例
奇怪的是,根据 this 使用 DEVICE_INT_ATTR
创建属性时无法正常工作,所以不确定这是怎么回事。
此外,我不是 100% 确定,但我认为 this 是在加载驱动程序时调用的,而不是在探测设备时调用的。
这是对 Sam Protsenko 和 Anthony 的回答的补充
如果您通过 DEVICE_ATTR
宏创建 设备 属性,则必须在 .dev_groups
中注册属性组 (mydrv_groups
)而不是 .groups
字段。
static struct device iio_evgen_dev = {
.bus = &iio_bus_type,
.dev_groups = iio_evgen_groups, // .dev_groups for DEVICE_ATTR
.groups = another_attr_group, // .groups for DRIVER_ATTR
.release = &iio_evgen_release,
};
然后属性会自动正确注册,而无需在 probe/remove 函数中设置任何内容,如 Greg Kroah-Hartman 的文章所述。
假设模块已经加载到内核中并且驱动程序注册在
/sys/bus/platform/drivers/mydrv
每个 设备 实例都将是驱动程序文件夹的子目录,如
/sys/bus/platform/drivers/mydrv/mydrv1
/sys/bus/platform/drivers/mydrv/mydrv2
在 .groups
字段中注册属性会在 driver 文件夹中创建属性。
在 .dev_groups
字段中注册属性会在 设备 的实例文件夹中创建属性。
mydrv
├── driver_attr1
├── driver_attr2
└── mydrv1
├── device_attr1
└── device_attr2
.groups
字段中属性的 show/store 函数无法通过 platform_set_drvdata(pdev, mydrv)
访问驱动程序数据集。
至少不是通过 dev_get_drvdata(dev)
访问它。
通过 dev_get_drvdata(dev)
returns NULL
访问驱动程序数据并取消引用它会导致内核 oops。
反过来,.dev_groups
字段中属性的 show/store 函数可以通过
struct mydrv *mydrv = dev_get_drvdata(dev);