如何将文件操作附加到平台驱动程序中的sysfs属性?

时间:2016-05-15 11:42:50

标签: linux-kernel linux-device-driver embedded-linux sysfs

我为我们开发的外围设备编写了一个平台驱动程序,并希望向sysfs公开一些配置选项。我已经设法使用探测器函数中的属性结构(见下文)和sysfs_create_file创建了相应的文件,但我无法弄清楚如何将show / store函数附加到平台驱动程序中的结构。 / p>

我在网上找到的大多数资源使用device_attribute结构或类似的东西来创建文件,这在哪里也适用?还有另一种方法可以为平台驱动程序执行此操作吗?

我的属性struct看起来像这样:

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);

3 个答案:

答案 0 :(得分:18)

归结为下一个:

  • 重新使用struct device(来自struct platform_device){/ 1}}的现有kobject(而不是创建自己的sysfs_create_group()
  • 使用kobject来声明DEVICE_ATTR()而非常规struct device_attribute,这会创建__ATTR()

以下是我为平台驱动程序创建sysfs属性的方法。

  1. 在sysfs属性(文件)的struct kobj_attribute / show()操作中创建您将用作私有数据的结构。例如:

    store()
  2. 在您的驱动程序struct mydrv { struct device *dev; long myparam; }; 中分配此结构:

    probe()
  3. 创建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()
  4. 为这些功能创建设备属性(在这些功能之后):

    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;
    }
    
  5. 为您的驱动程序声明属性表(实际列出 sysfs文件):

    static DEVICE_ATTR(myparam, S_IRUGO | S_IWUSR, mydrv_myparam_show,
                       mydrv_myparam_store);
    
  6. 声明属性组(实际上为驱动程序指定 sysfs目录):

    static struct attribute *mydrv_attrs[] = {
        &dev_attr_myparam.attr,
        NULL
    };
    

    实际上可以替换为一行:

    static struct attribute_group mydrv_group = {
        .name = "mydrv",
        .attrs = mydrv_attrs,
    };
    
    static struct attribute_group *mydrv_groups[] = {
        &mydrv_group,
        NULL
    }
    
  7. 在驱动程序的ATTRIBUTE_GROUPS(mydrv); 函数中创建sysfs目录和文件:

    probe()
  8. 删除驱动程序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; } ... } 函数中的sysfs文件:

    remove()
  9. 比赛条件备注

    正如@FranzForstmayr正确指出的那样,在static int mydrv_remove(struct platform_device *pdev) { sysfs_remove_group(&pdev->dev.kobj, &mydrv_group); ... } 中添加sysfs_create_group()的sysfs文件时,可能会有竞争条件。这是因为在调用mydrv_probe()之前可以通知用户空间这些文件存在(这些文件实际上是由mydrv_probe()函数创建的)。这个问题在Greg Kroah-Hartman撰写的"How to Create a sysfs File Correctly"文章中有详细介绍。

    因此,在sysfs_create_group()的情况下,您可以使用默认属性组,而不是调用platform_device(及其对应的sysfs_create_group())。为此,您需要将sysfs_remove_group()的相应.groups字段分配给您的属性组变量:

    struct device

    免责声明:由于代码this,我没有测试此代码,但它应该可以使用。

    有关上述竞争条件的更多见解,请参阅[1,2,3]链接。

    有关更多示例,请在内核源目录中运行next命令:

    static int mydrv_probe(struct platform_device *pdev)
    {
        ...
    
        pdev->dev.groups = mydrv_groups;
    
        ...
    }
    

    您也可以在提交消息中使用“default attribute”进行搜索:

    $ git grep -l --all-match -e platform_device -e attribute -e '\.groups =' -- drivers/
    

    我发现了一些提交:[4,5,6,7]。

    参考

    [1] My attributes are way too racy, what should I do?

    [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

答案 1 :(得分:0)

没有足够的声誉来发表评论,但我只想评论已接受答案中的默认属性组笔记。 我的理解是,不应如示例中所示在探测函数中添加它,而应在通常在您的末尾定义的设备结构(或device_driver,类或总线,取决于驱动程序)中进行设置文件。 例如:

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是在加载驱动程序时调用的,而不是在探测设备时调用的。

答案 2 :(得分:0)

这是对 Sam Protsenko 和 Anthony 回答的补充

如果您通过 DEVICE_ATTR 宏创建设备属性,那么您必须在 mydrv_groups 而不是 {{ 1}} 字段。

.dev_groups

然后属性会自动正确注册,无需在探测/删除函数中设置任何内容,如 Greg Kroah-Hartman 的文章中所述。

假设模块已经加载到内核中并且驱动程序

中注册
.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,
};

/sys/bus/platform/drivers/mydrv 字段中注册属性会在 driver 文件夹中创建属性。 在 /sys/bus/platform/drivers/mydrv/mydrv1 /sys/bus/platform/drivers/mydrv/mydrv2 字段中注册属性会在 device 的实例文件夹中创建属性。

.groups

.dev_groups 字段中属性的显示/存储功能无法通过 mydrv ├── driver_attr1 ├── driver_attr2 └── mydrv1 ├── device_attr1 └── device_attr2 访问驱动程序数据集。 至少不是通过 .groups 访问它。 通过 platform_set_drvdata(pdev, mydrv) 访问驱动程序数据返回 dev_get_drvdata(dev) 并取消引用它会导致内核 oops。

反过来,他在 dev_get_drvdata(dev) 字段中显示/存储属性的函数可以通过

访问驱动程序数据
NULL