Linux內核宏DEVICE_ATTR使用


1、前言

在Linux驅動程序編寫中,使用DEVICE_ATTR宏,可以定義一個struct device_attribute設備屬性,並使用sysfs的API函數,便可以在設備目錄下創建出屬性文件,當我們在驅動程序中實現了show和store函數后,便可以使用cat和echo命令對創建出來的設備屬性文件進行讀寫,從而達到控制設備的功能。

 

2、宏DEVICE_ATTR定義

在講解DEVICE_ATTR宏之前,先了解一些基本的結構體,首先是struct attribute結構體,其定義在include/linux/device.h中,結構體定義如下所示:

struct attribute {
    const char        *name;
    umode_t            mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
    bool            ignore_lockdep:1;
    struct lock_class_key    *key;
    struct lock_class_key    skey;
#endif
};

該結構體有兩個重要的成員,分別是name和mode,其中name代表屬性的名稱,一般表示為文件名,mode代表該屬性的讀寫權限,也就是屬性文件的讀寫權限。

關於文件的權限詳解,可以參考下面的鏈接:

https://blog.csdn.net/DLUTBruceZhang/article/details/8658475

接下來要了解的結構體為struct device_attribute,該結構體的定義在include /linux/device.h,其定義如下:

/* interface for exporting device attributes */
struct device_attribute {
    struct attribute    attr;
    ssize_t (*show)(struct device *dev, struct device_attribute *attr,
            char *buf);
    ssize_t (*store)(struct device *dev, struct device_attribute *attr,
             const char *buf, size_t count);
};

該結構體其實是struct attribute結構體的進一步封裝,並提供了兩個函數指針,show函數用於讀取設備的屬性文件,而store則是用於寫設備的屬性文件,當我們在Linux的驅動程序中實現了這兩個函數后,便可以使用cat和echo命令對設備屬性文件進行讀寫操作。

了解了一下基本的結構體,接下來,進一步分析宏DEVICE_ATTR的實現,在Linux內核源碼中,宏DEVICE_ATTR的定義在include/linux/device.h文件中,如下:

#define DEVICE_ATTR(_name, _mode, _show, _store) \
    struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)

而__ATTR宏的定義在include/linux/sysfs.h文件中,如下:

#define __ATTR(_name, _mode, _show, _store) {                \
    .attr = {.name = __stringify(_name),                \
         .mode = VERIFY_OCTAL_PERMISSIONS(_mode) },        \
    .show    = _show,                        \
    .store    = _store,                        \
}

通過上面的宏展開可以發現,其實宏DEVICE_ATTR實現的功能就是定義了一個struct device_attribute結構體變量dev_attr_name,並對里面的成員進行初始化,包括struct attribute結構體里面的name和mode成員變量,然后還有實現屬性文件讀寫的show和store函數賦值,非常簡單。

 

3、使用示例

接下來對宏DEVICE_ATTR使用示例進行分析,該示例在內核中將100個字節虛擬成一個設備,在驅動中實現設備屬性文件的讀寫函數,示例如下:

首先是設備屬性的定義,以及設備屬性文件讀寫函數的實現:

static ssize_t mydevice_show(struct device *dev, struct device_attribute *attr,
        char *buf)
{
    return sprintf(buf, "%s\n", mybuf);
}
static ssize_t mydevice_store(struct device *dev, struct device_attribute *attr,
         const char *buf, size_t count)
{
    sprintf(mybuf, "%s", buf);

    return count;
}
static DEVICE_ATTR(mydevice, 0644, mydevice_show, mydevice_store);

在上面的代碼中,使用宏DEVICE_ATTR定義了一個mydevice的屬性文件,而且這個屬性文件的讀寫權限為:文件所擁有者可讀寫,組和其他人只能讀。代碼還實現了該屬性文件的讀寫函數mydevice_show()和mydevice_store(),當在用戶空間中使用cat和echo命令時,將會調用到驅動程序中實現的兩個函數。

接下來是模塊的加載函數,當模塊加載時將會被調用,該函數的實現代碼如下:

static int __init mydevice_init(void)
{
    int ret;
    struct device *mydevice;
    
    major = register_chrdev(0, "mydevice", &myfops);
    if (major < 0) {
        ret = major;
        return ret;
    }
    
    myclass = class_create(THIS_MODULE, "myclass");
    if (IS_ERR(myclass)) {
        ret = -EBUSY;
        goto fail;
    }
    
    mydevice = device_create(myclass, NULL, MKDEV(major, 0), NULL, "mydevice");
    if (IS_ERR(mydevice)) {
        class_destroy(myclass);
        ret = -EBUSY;
        goto fail;
    }

    ret = sysfs_create_file(&mydevice->kobj, &dev_attr_mydevice.attr);
    if (ret < 0)
        return ret;

    return 0;

fail:
    unregister_chrdev(major, "mydevice");
    return ret;
}

函數首先調用register_chrdev()完成一個主設備號的動態申請,設備的名稱為mydevice,然后調用class_create()和device_create()在sysfs中動態創建出設備所屬的類myclass和mydevice設備,需要注意的是,這兩個函數調用后,要對返回的結果進行錯誤檢測,最后,使用sysfs的API函數sysfs_create_file()在sysfs中創建出設備的屬性文件,完成驅動模塊的加載。

接下來是該模塊的卸載函數,函數的代碼如下所示:

static void __exit mydevice_exit(void)
{
    device_destroy(myclass, MKDEV(major, 0));
    class_destroy(myclass);
    unregister_chrdev(major, "mydevice");
}

模塊的卸載函數與模塊的加載函數是相對的操作,需要調用device_destory()和class_destory()對模塊加載創建的myclass和mydevice進行銷毀,然后調用unregister_chrdev()將動態分配的主設備號進行釋放。

驅動程序完整代碼如下:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/sysfs.h>
#include <linux/string.h>
#include <linux/device.h>
#include <linux/fs.h>

static char mybuf[100] = "mydevice";
static int major;
static struct class *myclass;

static ssize_t mydevice_show(struct device *dev, struct device_attribute *attr,
        char *buf)
{
    return sprintf(buf, "%s\n", mybuf);
}
static ssize_t mydevice_store(struct device *dev, struct device_attribute *attr,
         const char *buf, size_t count)
{
    sprintf(mybuf, "%s", buf);

    return count;
}
static DEVICE_ATTR(mydevice, 0644, mydevice_show, mydevice_store);

static struct file_operations myfops = {
    .owner = THIS_MODULE,
};

static int __init mydevice_init(void)
{
    int ret;
    struct device *mydevice;
    
    major = register_chrdev(0, "mydevice", &myfops);
    if (major < 0) {
        ret = major;
        return ret;
    }
    
    myclass = class_create(THIS_MODULE, "myclass");
    if (IS_ERR(myclass)) {
        ret = -EBUSY;
        goto fail;
    }
    
    mydevice = device_create(myclass, NULL, MKDEV(major, 0), NULL, "mydevice");
    if (IS_ERR(mydevice)) {
        class_destroy(myclass);
        ret = -EBUSY;
        goto fail;
    }

    ret = sysfs_create_file(&mydevice->kobj, &dev_attr_mydevice.attr);
    if (ret < 0)
        return ret;

    return 0;

fail:
    unregister_chrdev(major, "mydevice");
    return ret;
}

static void __exit mydevice_exit(void)
{
    device_destroy(myclass, MKDEV(major, 0));
    class_destroy(myclass);
    unregister_chrdev(major, "mydevice");
}

module_init(mydevice_init);
module_exit(mydevice_exit);

MODULE_DESCRIPTION("A simplest driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("HLY");

 

4、測試結果

將驅動程序進行編譯后,將驅動模塊進行加載,並查看創建出來的屬性文件,使用cat和echo命令進行讀寫測試:

# make
# insmod simple-device.ko
# cd /sys/devices/virtual/myclass/mydevice/
# ls –al ./

結果如下所示,可以看到創建出來的屬性文件mydevice:

使用cat和echo命令進行文件讀寫測試:

# cat mydevice
# echo "I am a simplest driver." > mydevice
# cat mydevice

運行結果如下所示,可以看到,能使用cat和echo命令正常完成屬性文件的讀寫了:

 

5、小節

本文簡單介紹了Linux內核中DEVICE_ATTR宏的實現,並使用一個簡單的驅動程序示例來介紹了如何在Linux驅動程序中使用DEVICE_ATTR宏以及實現屬性文件的讀寫函數。

 

參考:

https://blog.csdn.net/hpu11/article/details/83113729

https://blog.csdn.net/DLUTBruceZhang/article/details/8658475

《LINUX設備驅動程序(第三版)》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM