DEVICE_ATTR

sysfs接口函數的建立_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,
    };

在利用sysfs_create_group(&pdev->dev.kobj, &dev_attr_grp);創建接口

       通過以上簡單的三個步驟,就可以在adb shell 終端查看到接口了。當我們將數據 echo 到接口中時,在上層實際上完成了一次 write 操作,對應到 kernel ,調用了驅動中的 “store”。同理,當我們cat 一個 接口時則會調用 “show” 。到這裏,只是簡單的建立了 android 層到 kernel 的橋樑,真正實現對硬件操作的,還是在 "show" 和 "store" 中完成的。


       其實呢?!用個proc文件系統的就知道,這個就和proc中的write和read一樣的,以我的理解:proc有點老了,以後肯定會大量使用attribute,proc好比是Windows XP,attribute就像是Windows Seven



###############################################################

爲了更好地瞭解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 *);

發佈了11 篇原創文章 · 獲贊 6 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章