【问题标题】:How to attach file operations to sysfs attribute in platform driver?如何将文件操作附加到平台驱动程序中的 sysfs 属性?
【发布时间】:2016-05-15 11:42:49
【问题描述】:

我为我们开发的外围设备编写了一个平台驱动程序,并希望向 sysfs 公开一些配置选项。我已经设法在探测函数中使用属性结构(见下文)和sysfs_create_file 创建了适当的文件,但我不知道如何将显示/存储函数附加到平台驱动程序中的结构。

我在网上找到的大多数资源都使用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);

【问题讨论】:

  • 您使用的是什么内核版本?用于创建 sysfs 文件的 API 在某些时候发生了重大变化,因此您可以找到不同的方法来创建这些文件。
  • 我使用的是内核 4.1

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


【解决方案1】:

归结为下一个:

  • struct device(来自您的struct platform_device)中的现有kobject 重用于sysfs_create_group()(而不是创建您自己的kobject
  • 使用DEVICE_ATTR() 声明struct device_attribute 而不是常规的__ATTR(),这会创建struct kobj_attribute

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

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

    struct mydrv {
        struct device *dev;
        long myparam;
    };
    
  2. 在你的驱动程序的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);
    
        ...
    }
    
  3. 创建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;
    }
    
  4. 为这些功能创建设备属性(就在这些功能之后):

    static DEVICE_ATTR(myparam, S_IRUGO | S_IWUSR, mydrv_myparam_show,
                       mydrv_myparam_store);
    
  5. 声明属性表(实际上为您的驱动程序列出 sysfs 文件):

    static struct attribute *mydrv_attrs[] = {
        &dev_attr_myparam.attr,
        NULL
    };
    
  6. 声明属性组(实际上是为您的驱动程序指定 sysfs 目录):

    static struct attribute_group mydrv_group = {
        .name = "mydrv",
        .attrs = mydrv_attrs,
    };
    
    static struct attribute_group *mydrv_groups[] = {
        &mydrv_group,
        NULL
    }
    

    其实可以换成一行:

    ATTRIBUTE_GROUPS(mydrv);
    
  7. 在驱动程序的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;
        }
    
        ...
    }
    
  8. 删除驱动程序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 文件时可能存在竞争条件。这是因为在调用mydrv_probe() 之前已经通知用户空间这些文件存在(这些文件实际上是由sysfs_create_group() 函数创建的)。这个问题在 Greg Kroah-Hartman 的 "How to Create a sysfs File Correctly" 文章中有详细介绍。

因此,在我们的platform_device 示例中,您可以使用默认属性组,而不是调用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/

您还可以在提交消息中通过“默认属性”进行搜索:

$ git log --no-merges --oneline --grep="default attribute" -- 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

【讨论】:

  • 你能解释一下如何根据 Greg KH 的帖子调整这个解决方案吗? kroah.com/log/blog/2013/06/26/…
  • @FranzForstmayr 这是一个有效的观点。坦白说,我对此一无所知。但我在回答中添加了比赛条件说明,解释了如何使用您提到的文章中的见解。
【解决方案2】:

没有足够的声誉发表评论,但我只想评论接受答案中的默认属性组注释。 我的理解是,这不应该像示例中给出的那样添加到探测函数中,而是应该在设备结构(或 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 是在加载驱动程序时调用的,而不是在探测设备时调用的。

【讨论】:

    【解决方案3】:

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

    如果您通过DEVICE_ATTR 宏创建设备 属性,那么您必须在.dev_groups 而不是.groups 字段中注册属性组 (mydrv_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,
    };
    

    然后属性会自动正确注册,而无需在探测/删除函数中设置任何内容,如 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 字段中属性的显示/存储功能无法通过platform_set_drvdata(pdev, mydrv) 访问驱动程序数据集。 至少不能通过dev_get_drvdata(dev) 访问它。 通过dev_get_drvdata(dev) 访问驱动程序数据返回NULL 并取消引用它会导致内核错误。

    反过来,他在.dev_groups字段中显示/存储属性的函数可以访问驱动程序数据

    struct mydrv *mydrv = dev_get_drvdata(dev);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-03
      相关资源
      最近更新 更多