一、前言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設備驅動程序(第三版)》