DEVICE_ATTR
說道sysfs接口,就不得不提到函數宏?DEVICE_ATTR,原型是
#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
函數宏DEVICE_ATTR內封裝的是__ATTR(_name,_mode,_show,_stroe)方法
_show:表示的是讀方法,
_stroe表示的是寫方法。
?
當然_ATTR不是獨生子女,他還有一系列的姊妹__ATTR_RO宏只有讀方法,__ATTR_NULL等等
如對設備的使用????? ??DEVICE_ATTR???
對驅動使用??????? ???????DRIVER_ATTR
對總線使用??????? ?? ????BUS_ATTR?
對類別?(class)?使用??CLASS_ATTR
這四個高級的宏來自于<include/linux/device.h>?
DEVICE_ATTR??宏聲明有四個參數,分別是名稱、權限位、讀函數、寫函數。其中讀函數和寫函數是讀寫功能函數的函數名。
如果你完成了DEVICE_ATTR函數宏的填充,下面就需要創建接口了
例如:
? ??static DEVICE_ATTR(polling, S_IRUGO | S_IWUSR,?show_polling, set_polling);
? ? static struct attribute *dev_attrs[] = {
? ? ? ? ? ??&dev_attr_polling.attr,
? ? ? ? ? ? NULL,
? ? };
當你想要實現的接口名字是polling的時候,需要實現結構體struct attribute?*dev_attrs[]
其中成員變量的名字必須是&dev_attr_polling.attr
然后再封裝
? ??static struct attribute_group dev_attr_grp = {
? ? ? ? ? ? .attrs = dev_attrs,
? ? };
?????? 通過以上簡單的三個步驟,就可以在adb?shell?終端查看到接口了。當我們將數據?echo?到接口中時,在上層實際上完成了一次?write?操作,對應到?kernel?,調用了驅動中的?“store”。同理,當我們cat?一個?接口時則會調用?“show”?。到這里,只是簡單的建立了?android?層到?kernel?的橋梁,真正實現對硬件操作的,還是在?"show"?和?"store"?中完成的。
###############################################################
sysfs文件系統學習
為了更好地了解kobject的層次關系,有必要了解一下這種層次關系的表現機制:sysfs。本文簡單地學習了一下sysfs,大部分內容來自內核文檔sysfs.txt。好了,開始我們的學習之旅,呵呵。
?
何為sysfs
??? sysfs是一種基于ram的文件系統,它提供了一種用于向用戶空間展現內核空間里的對象、屬性和鏈接。sysfs與kobject層次緊密相連,它將kobject層次關系表現出來,使得用戶空間可以看見這些層次關系。
??? 在控制臺輸入命令“mount -t sysfs sysfs /sys”,就可以在/sys目錄下看到這些層次關系了。
?
目錄的創建
??? 對于每個注冊到系統的kobject,在sysfs中都有一個目錄來展現它,這個目錄(AA)會作為某個目錄(A)的子目錄而被創建,我們知道目錄AA代表kobject,那么目錄A則代表kobject->parent,顯示這種目錄層次關系可以很好地向用戶展現kobject層次結構。在sysfs中位于頂層的那些目錄,分別代表著不同的子系統,每個新加入的kobject都應該歸屬于某一個子系統。
????sysfs會把目錄所代表的kobject存儲在目錄的dentry結構的d_fsdata字段,這樣當文件打開和關閉的時候,sysfs可以直接對kobject做引用計數。
?
屬性
??? 在sysfs中,kobject的屬性表現為一個普通的文件。sysfs將文件的I/O操作重定向到那些為該屬性定義的方法,提供了一種讀寫屬性的機制。
?????屬性應該表現為ASCII文本文件,并且最好每個文件只包含一個值。當然,每個文件僅包含一個值可能會有害于效率,所以如果一個文件包含某種類型的數據的數組也是被接受的。不建議在一個文件中包含不同數據類型的數據和多行數據。
??? 屬性的定義如下:
??? struct attribute {
??????? char??????????????????? * name;
??????? struct module??*owner;
??????? mode_t????????????????? mode;
??? };
??? int sysfs_create_file(struct kobject * kobj, const struct attribute * attr);
??????? 在kobj所在目錄下創建一個屬性文件,文件名為attr->name
??? void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr);
??????? 將屬性文件attr->name從kobj所在目錄下移除
?
??? 為了使對屬性的讀寫變得有意義,一般將attribute結構嵌入到其他數據結構中。子系統通常都會定義自己的屬性結構,并且提供添加和刪除屬性文件的包裹函數。
??? 例如,設備驅動模型為device子系統定義了相應的屬性結構device_attribute:
??? 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);
??? };
?
??? int device_create_file(struct device *, struct device_attribute *);
??????? 在/sys/devices/xxx/目錄下創建device屬性文件
??? void device_remove_file(struct device *, struct device_attribute *);
??????? 移除/sys/devices/xxx/目錄下的device屬性文件
?
??? 系統提供了一個宏方便定義device屬性:
??? #define DEVICE_ATTR(_name, _mode, _show, _store) /
??????? struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
??
? ? 其中,__ATTR定義如下:
??? #define __ATTR(_name,_mode,_show,_store) { /
???? .attr = {.name = __stringify(_name), .mode = _mode },?/
???? .show?= _show,?????/
???? .store?= _store,?????/
??? }
???
??? 例如,定義一個device屬性,名為foo,讀寫該文件的方法分別為show_foo和store_foo:
??? static DEVICE_ATTR(foo, S_IWUSR | S_IRUGO, show_foo, store_foo);
??? 將宏展開為:???
??? static struct device_attribute dev_attr_foo = {
??????? ??? .attr?= {
????????????????????????? .name = "foo",
????????????????????????? .mode = S_IWUSR | S_IRUGO,
???????????? ????????? },
??????????? .show = show_foo,
??????????? .store = store_foo,
????};
?
子系統特定的回調函數
??? 當子系統定義一個新的屬性類型時,必須實現一組sysfs操作,從而將文件的讀寫調用(read/write調用)重定向到屬性擁有者的show和store方法。
?
??? struct sysfs_ops {
??????? ssize_t (*show)(struct kobject *, struct attribute *, char *);
??????? ssize_t (*store)(struct kobject *, struct attribute *, const char *);
??? };
?
??? 當讀寫一個文件時,sysfs將為此類型調用合適的方法,這些方法會將kobject結構和attribute結構轉換為合適的指針類型,然后調用與之關聯的相關的方法。注意,子系統必須已經為此類型定義好了kobj_type作為此類型的描述符,因為sysfs_ops指針存儲在kobj_type中。
??? 舉個例子:??
??? #define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
??? #define to_dev(d) container_of(d, struct device, kobj)
??? static ssize_t dev_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
??? {
??????? struct device_attribute * dev_attr = to_dev_attr(attr);
??????? struct device * dev = to_dev(kobj);
??????? ssize_t ret = 0;
??????? if (dev_attr->show)
??????????????? ret = dev_attr->show(dev, buf);
??????? return ret;
??? }
?
屬性的讀寫
??? 為了讀寫屬性,當定義屬性時,必須指定show和/或者store方法:
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);
?
sysfs allocates a buffer of size (PAGE_SIZE) and passes it to the
method. Sysfs will call the method exactly once for each read or
write. This forces the following behavior on the method
implementations:
?
- On read(2), the show() method should fill the entire buffer.?
? Recall that an attribute should only be exporting one value, or an
? array of similar values, so this shouldn't be that expensive.
? This allows userspace to do partial reads and forward seeks
? arbitrarily over the entire file at will. If userspace seeks back to
? zero or does a pread(2) with an offset of '0' the show() method will
? be called again, rearmed, to fill the buffer.
- On write(2), sysfs expects the entire buffer to be passed during the
? first write. Sysfs then passes the entire buffer to the store()
? method.?
??
??? 當寫一個sysfs文件時,用戶空間的進程應該先讀取整個文件,然后修改希望改變的值,最后將整個緩沖區回寫到文件中。
??? Attribute method implementations should operate on an identical buffer when reading and writing values.
???
??? 其他注意事項:
- Writing causes the show() method to be rearmed regardless of current file position.(???)
- 緩沖區的大小應總是為PAGE_SIZE個字節。在i386上,PAGE_SIZE=4096
- show方法應該返回放入緩沖區的字節數,即snprintf的返回值
- show方法應該總是使用snprintf
- store方法應該返回實際使用的字節數,可以使用strlen來得到
- show和/或者store方法可能會出錯,所以當失敗時,記得返回錯誤值
- The object passed to the methods will be pinned in memory via sysfs
? referencing counting its embedded object. However, the physical?
? entity (e.g. device) the object represents may not be present. Be?
? sure to have a way to check this, if necessary.?(???)
??? 下面的代碼展示了device屬性的一個簡單的實現:
??? static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf)
??? {
???????? return snprintf(buf, PAGE_SIZE, "%s/n", dev->name);
??? }
??? static ssize_t store_name(struct device * dev, const char * buf)
??? {
???????? sscanf(buf, "%20s", dev->name);
???????? return strnlen(buf, PAGE_SIZE);
????}
??? static DEVICE_ATTR(name, S_IRUGO, show_name, store_name);
??? 注意,實際應用時,并不允許從用戶空間設置設備的名字,這里僅舉個例子。
?
sysfs頂層目錄結構
??? sysfs目錄結構展現了內核數據結構之間的關系,頂層目錄結構如下:
?
block/
bus/
class/
dev/
devices/
firmware/
net/
fs/
?
??? 下面撿幾個重要的目錄進行說明:
??? devices目錄展現了系統中的設備樹,它直接對應于內核中的設備樹,即device的層次結構?;
??? bus目錄下包含了若干目錄,每個目錄表示系統中的一個總線,且每個目錄下又包含兩個子目錄:devices、drivers。其中devices子目錄下包含系統中發現的device的符號鏈接,這些符號鏈接分別指向/sys/devices/xxx目錄下對應的目錄;drivers子目錄下包含特定總線上的那些用于驅動每個設備的驅動程序的目錄(可見,一個驅動程序只會出現在某一個特定的總線上);
???
當前接口
??? 目前sysfs中存在以下接口:
- devices (include/linux/device.h)
??? device屬性:
??? 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);
??? };
?
??? 屬性的聲明:
??? DEVICE_ATTR(_name, _mode, _show, _store);
??? 屬性的創建和移除:
??? int device_create_file(struct device *device, struct device_attribute * attr);
??? void device_remove_file(struct device * dev, struct device_attribute * attr);
- bus drivers (include/linux/device.h)
??? bus屬性:
??? struct bus_attribute {
??????? struct attribute??????? attr;
??????? ssize_t (*show)(struct bus_type *, char * buf);
??????? ssize_t (*store)(struct bus_type *, const char * buf);
??? };
??? 屬性的聲明:
??? BUS_ATTR(_name, _mode, _show, _store)
??? 屬性的創建和移除:
??? int bus_create_file(struct bus_type *, struct bus_attribute *);
??? void bus_remove_file(struct bus_type *, struct bus_attribute *);
- device drivers (include/linux/device.h)
??? driver屬性:
??? struct driver_attribute {
??????? struct attribute??????? attr;
??????? ssize_t (*show)(struct device_driver *, char * buf);
??????? ssize_t (*store)(struct device_driver *, const char * buf,
???????????????????????? size_t count);
??? };
??? 屬性的聲明:
??? DRIVER_ATTR(_name, _mode, _show, _store)
??? 屬性的創建和移除:
??? int driver_create_file(struct device_driver *, struct driver_attribute *);
??? void driver_remove_file(struct device_driver *, struct driver_attribute *);
總結
以上是生活随笔為你收集整理的DEVICE_ATTR的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 内核驱动中常见的miscdevice、p
- 下一篇: Android JNI 使用的数据结构J