Linux內核宏DEVICE_ATTR使用

一、前言linux

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

 

二、宏DEVICE_ATTR定義測試

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

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表明該屬性的讀寫權限,也就是屬性文件的讀寫權限。.net

關於文件的權限詳解,能夠參考下面的連接:指針

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

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

/* 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文件中,以下:string

#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函數賦值,很是簡單。

 

三、使用示例

接下來對宏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");

 

四、測試結果

將驅動程序進行編譯後,將驅動模塊進行加載,並查看建立出來的屬性文件,使用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命令正常完成屬性文件的讀寫了:

 

五、小節

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

 

參考:

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

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

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

相關文章
相關標籤/搜索