sysfs接口函數的創建_DEVICE_ATTR

說道sysfs接口,就不得不提到函數宏 DEVICE_ATTR,原型是 linux

#define DEVICE_ATTR(_name, _mode, _show, _store) \ android

struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
shell

函數宏DEVICE_ATTR內封裝的是__ATTR(_name,_mode,_show,_stroe)方法 數組

_show:表示的是讀方法, 數據結構

_stroe表示的是寫方法。 less

 

固然_ATTR不是獨生子女,他還有一系列的姊妹__ATTR_RO宏只有讀方法,__ATTR_NULL等等 ide

如對設備的使用        DEVICE_ATTR    函數

對驅動使用               DRIVER_ATTR 學習

對總線使用               BUS_ATTR  this

對類別 (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 *);

相關文章
相關標籤/搜索