總線設備驅動模型

Linux設備模型  總線

總線是處理器和一個或多個設備之間的通道,在設備模型中, 所有的設備都通過總線相連, 甚至是內部的虛擬"platform"總線。總線可以相互插入。設備模型展示了總線和它們所控制的設備之間的實際連接。
在 Linux 設備模型中, 總線由 bus_type 結構表示, 定義在 <linux/device.h> :

struct bus_type {
    const char        * name;/*總線類型名稱*/
    struct module        * owner;/*指向模塊的指針(如果有), 此模塊負責操作這個總線*/

    struct kset        subsys;/*與該總線相關的子系統*/
    struct kset        drivers;/*總線驅動程序的kset*/
    struct kset        devices;/* 掛在該總線的所有設備的kset*/

    struct klist        klist_devices;/*與該總線相關的驅動程序鏈表*/
    struct klist        klist_drivers;/*掛接在該總線的設備鏈表*/

    struct blocking_notifier_head bus_notifier;

    struct bus_attribute    * bus_attrs; /*總線屬性*/
    struct device_attribute * dev_attrs; /*設備屬性,指向爲每個加入總線的設備建立的默認屬性鏈表*/
    struct driver_attribute * drv_attrs; /*驅動程序屬性*/
    struct bus_attribute drivers_autoprobe_attr;/*驅動自動探測屬性*/
    struct bus_attribute drivers_probe_attr;/*驅動探測屬性*/

    int        (*match)(struct device * dev, struct device_driver * drv);
    int        (*uevent)(struct device *dev, char **envp,
                 int num_envp, char *buffer, int buffer_size);
    int        (*probe)(struct device * dev);
    int        (*remove)(struct device * dev);
    void        (*shutdown)(struct device * dev);

    int (*suspend)(struct device * dev, pm_message_t state);
    int (*suspend_late)(struct device * dev, pm_message_t state);
    int (*resume_early)(struct device * dev);
    nt (*resume)(struct device * dev);
/*處理熱插拔、電源管理、探測和移除等事件的方法*/
    unsigned int drivers_autoprobe:1;
};

 

在更新的內核裏,這個結構體變得更簡潔了,隱藏了無需驅動編程人員知道的一些成員:

 

 

/*in Linux 2.6.26.5*/

struct bus_type {
    const char        *name;
    struct bus_attribute    *bus_attrs;
    struct device_attribute    *dev_attrs;
    struct driver_attribute    *drv_attrs;

    int (*match)(struct device *dev, struct device_driver *drv);
    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);
    void (*shutdown)(struct device *dev);

    int (*suspend)(struct device *dev, pm_message_t state);
    int (*suspend_late)(struct device *dev, pm_message_t state);
    int (*resume_early)(struct device *dev);
    int (*resume)(struct device *dev);

    struct bus_type_private *p;
};

struct bus_type_private {
    struct kset subsys;
    struct kset *drivers_kset;
    struct kset *devices_kset;
    struct klist klist_devices;
    struct klist klist_drivers;
    struct blocking_notifier_head bus_notifier;
    unsigned int drivers_autoprobe:1;
    struct bus_type *bus;
};

 

總線的註冊和刪除

總線的主要註冊步驟:

(1)申明和初始化 bus_type 結構體。只有很少的 bus_type 成員需要初始化,大部分都由設備模型核心控制。但必須爲總線指定名字及一些必要的方法。例如:

struct bus_type ldd_bus_type = {
    .name = "ldd",
    .match = ldd_match,
    .uevent = ldd_uevent,
};

(2)調用bus_register函數註冊總線。

int bus_register(struct bus_type * bus)

調用可能失敗, 所以必須始終檢查返回值。若成功,新的總線子系統將被添加進系統,並可在sysfs 的 /sys/bus 下看到。之後可以向總線添加設備。

例如:

ret = bus_register(&ldd_bus_type);
if (ret)
 return ret;

  

當必須從系統中刪除一個總線時, 調用:

void bus_unregister(struct bus_type *bus);

總線方法

在 bus_type 結構中定義了許多方法,它們允許總線核心作爲設備核心和單獨的驅動程序之間提供服務的中介,主要介紹以下兩個方法:

int (*match)(struct device * dev, struct device_driver * drv);
/*當一個新設備或者驅動被添加到這個總線時,這個方法會被調用一次或多次,若指定的驅動程序能夠處理指定的設備,則返回非零值。必須在總線層使用這個函數, 因爲那裏存在正確的邏輯,核心內核不知道如何爲每個總線類型匹配設備和驅動程序*/

int (*uevent)(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size);
/*在爲用戶空間產生熱插拔事件之前,這個方法允許總線添加環境變量(參數和 kset 的uevent方法相同)*/

lddbus的match和uevent方法:

static int ldd_match(struct device *dev, struct device_driver *driver)
{
 return !strncmp(dev->bus_id, driver->name, strlen(driver->name));
}/*僅簡單比較驅動和設備的名字*/
/*當涉及實際硬件時, match 函數常常對設備提供的硬件 ID 和驅動所支持的 ID 做比較*/

static int ldd_uevent(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size)
{
 envp[0] = buffer;
 if (snprintf(buffer, buffer_size, "LDDBUS_VERSION=%s",
 Version) >= buffer_size)
 return -ENOMEM;
 envp[1] = NULL;
 return 0;
}/*在環境變量中加入 lddbus 源碼的當前版本號*/

對設備和驅動的迭代

若要編寫總線層代碼, 可能不得不對所有已經註冊到總線的設備或驅動進行一些操作,這可能需要仔細研究嵌入到 bus_type 結構中的其他數據結構, 但最好使用內核提供的輔助函數:

int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *, void *));
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, void *data, int (*fn)(struct device_driver *, void *));

/*這兩個函數迭代總線上的每個設備或驅動程序, 將關聯的 device 或 device_driver 傳遞給 fn, 同時傳遞 data 值。若 start 爲 NULL, 則從第一個設備開始; 否則從 start 之後的第一個設備開始。若 fn 返回非零值, 迭代停止並且那個值從 bus_for_each_dev 或bus_for_each_drv 返回。*/

總線屬性

幾乎 Linux 設備模型中的每一層都提供添加屬性的函數,總線層也不例外。bus_attribute 類型定義在 <linux/device.h> 如下:

struct bus_attribute {
    struct attribute    attr;
    ssize_t (*show)(struct bus_type *, char * buf);
    ssize_t (*store)(struct bus_type *, const char * buf, size_t count);
};


可以看出struct bus_attribute 和struct attribute 很相似,其實大部分在 kobject 級上的設備模型層都是以這種方式工作。

內核提供了一個宏在編譯時創建和初始化 bus_attribute 結構:

BUS_ATTR(_name,_mode,_show,_store)/*這個宏聲明一個結構, 將 bus_attr_ 作爲給定 _name 的前綴來創建總線的真正名稱*/

/*總線的屬性必須顯式調用 bus_create_file 來創建:*/
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr); 

/*刪除總線的屬性調用:*/
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr);

例如創建一個包含源碼版本號簡單屬性文件方法如下:

static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
 return snprintf(buf, PAGE_SIZE, "%s\n", Version);
}

static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL); 

/*在模塊加載時創建屬性文件:*/
if (bus_create_file(&ldd_bus_type, &bus_attr_version))
 printk(KERN_NOTICE "Unable to create version attribute\n");

/*這個調用創建一個包含 lddbus 代碼的版本號的屬性文件(/sys/bus/ldd/version)*/


 

設備

在最底層, Linux 系統中的每個設備由一個 struct device 代表:

struct device {
    struct klist        klist_children;
    struct klist_node    knode_parent;   /* node in sibling list */
    struct klist_node    knode_driver;
    struct klist_node    knode_bus;
    struct device        *parent;/* 設備的 "父" 設備,該設備所屬的設備,通常一個父設備是某種總線或者主控制器. 如果 parent 是 NULL, 則該設備是頂層設備,較少見 */

    struct kobject kobj;/*代表該設備並將其連接到結構體系中的 kobject; 注意:作爲通用的規則, device->kobj->parent 應等於 device->parent->kobj*/
    char    bus_id[BUS_ID_SIZE];/*在總線上唯一標識該設備的字符串;例如: PCI 設備使用標準的 PCI ID 格式, 包含:域, 總線, 設備, 和功能號.*/
    struct device_type    *type;
    unsigned        is_registered:1;
    unsigned        uevent_suppress:1;
    struct device_attribute uevent_attr;
    struct device_attribute *devt_attr;

    struct semaphore    sem;  /* semaphore to synchronize calls to its driver. */
    struct bus_type    * bus;     /*標識該設備連接在何種類型的總線上*/
    struct device_driver *driver;    /*管理該設備的驅動程序*/
    void        *driver_data;    /*該設備驅動使用的私有數據成員*/
    void        *platform_data;    /* Platform specific data, device core doesn't touch it */
    struct dev_pm_info    power;

#ifdef CONFIG_NUMA
    int        numa_node;   /* NUMA node this device is close to */
#endif
    u64        *dma_mask;    /* dma mask (if dma'able device) */
    u64        coherent_dma_mask;/* Like dma_mask, but for
                     alloc_coherent mappings as
                     not all hardware supports
                     64 bit addresses for consistent
                     allocations such descriptors. */

    struct list_head    dma_pools;    /* dma pools (if dma'ble) */

    struct dma_coherent_mem    *dma_mem; /* internal for coherent mem override */
    /* arch specific additions */
    struct dev_archdata    archdata;

    spinlock_t        devres_lock;
    struct list_head    devres_head;

    /* class_device migration path */
    struct list_head    node;
    struct class        *class;
    dev_t          devt;       /* dev_t, creates the sysfs "dev" */
    struct attribute_group    **groups;    /* optional groups */

    void    (*release)(struct device * dev);/*當這個設備的最後引用被刪除時,內核調用該方法; 它從被嵌入的 kobject 的 release 方法中調用。所有註冊到核心的設備結構必須有一個 release 方法, 否則內核將打印錯誤信息*/
};
/*在註冊 struct device 前,最少要設置parent, bus_id, bus, 和 release 成員*/

設備註冊

設備的註冊和註銷函數爲:

int device_register(struct device *dev);
void device_unregister(struct device *dev);

一個實際的總線也是一個設備,所以必須單獨註冊,以下爲 lddbus 在編譯時註冊它的虛擬總線設備源碼:

static void ldd_bus_release(struct device *dev)
{
 printk(KERN_DEBUG "lddbus release\n");
}

struct device ldd_bus = {
 .bus_id = "ldd0",
 .release = ldd_bus_release

}; /*這是頂層總線,parent 和 bus 成員爲 NULL*/

/*作爲第一個(並且唯一)總線, 它的名字爲 ldd0,這個總線設備的註冊代碼如下:*/
ret = device_register(&ldd_bus);
if (ret)
 printk(KERN_NOTICE "Unable to register ldd0\n");
/*一旦調用完成, 新總線會在 sysfs 中 /sys/devices 下顯示,任何掛到這個總線的設備會在 /sys/devices/ldd0 下顯示*/

設備屬性

sysfs中的設備入口可有屬性,相關的結構是:

/* interface for exporting device attributes 這個結構體和《LDD3》中的不同,已經被更新過了,請特別注意!*/
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);
/*這個宏聲明一個結構, 將 dev_attr_ 作爲給定 _name 的前綴來命名設備屬性

/*屬性文件的實際處理使用以下函數:*/
 int device_create_file(struct device *device,    struct device_attribute * entry);
 void device_remove_file(struct device * dev, struct device_attribute * attr);

設備結構的嵌入

device結構包含設備模型核心用來模擬系統的信息。但大部分子系統記錄了關於它們又擁有的設備的額外信息,所以很少單純用 device 結構代表設備,而是,通常將其嵌入一個設備的高層表示中。底層驅動幾乎不知道 struct device。

lddbus驅動創建了它自己的 device 類型,並期望每個設備驅動使用這個類型來註冊它們的設備:

struct ldd_device {
 char *name;
 struct ldd_driver *driver;
 struct device dev; 
}; 
#define to_ldd_device(dev) container_of(dev, struct ldd_device, dev);

lddbus導出的註冊和註銷接口如下:

/*
 * LDD devices.
 */

/*
 * For now, no references to LDDbus devices go out which are not
 * tracked via the module reference count, so we use a no-op
 * release function.
 */
static void ldd_dev_release(struct device *dev)
{ }

int register_ldd_device(struct ldd_device *ldddev)
{
    ldddev->dev.bus = &ldd_bus_type;
    ldddev->dev.parent = &ldd_bus;
    ldddev->dev.release = ldd_dev_release;
    strncpy(ldddev->dev.bus_id, ldddev->name, BUS_ID_SIZE);
    return device_register(&ldddev->dev);
}
EXPORT_SYMBOL(register_ldd_device);

void unregister_ldd_device(struct ldd_device *ldddev)
{
    device_unregister(&ldddev->dev);
}
EXPORT_SYMBOL(unregister_ldd_device);

 

 sculld驅動添加一個自己的屬性到它的設備入口,稱爲dev, 僅包含關聯的設備號,源碼如下:

static ssize_t sculld_show_dev(struct device *ddev,struct device_attribute *attr, char *buf)
{
 struct sculld_dev *dev = ddev->driver_data;
 return print_dev_t(buf, dev->cdev.dev);
}

static DEVICE_ATTR(dev, S_IRUGO, sculld_show_dev, NULL);

/*接着, 在初始化時間, 設備被註冊, 並且 dev 屬性通過下面的函數被創建:*/
static void sculld_register_dev(struct sculld_dev *dev, int index) 
{
 sprintf(dev->devname, "sculld%d", index);
 dev->ldev.name = dev->devname;
 dev->ldev.driver = &sculld_driver;
 dev->ldev.dev.driver_data = dev;
 register_ldd_device(&dev->ldev);
 if (device_create_file(&dev->ldev.dev, &dev_attr_dev)) 
    printk( "Unable to create dev attribute ! \n"); 
} /*注意:程序使用 driver_data 成員來存儲指向我們自己的內部的設備結構的指針。請檢查device_create_file的返回值,否則編譯時會有警告。*/


 

設備驅動程序

設備模型跟蹤所有系統已知的驅動,主要目的是使驅動程序核心能協調驅動和新設備之間的關係。一旦驅動在系統中是已知的對象就可能完成大量的工作。驅動程序的結構體 device_driver 定義如下:

/*定義在<linux/device.h>*/
struct device_driver {
    const char        * name;/*驅動程序的名字( 在 sysfs 中出現 )*/
    struct bus_type        * bus;/*驅動程序所操作的總線類型*/

    struct kobject        kobj;/*內嵌的kobject對象*/
    struct klist        klist_devices;/*當前驅動程序能操作的設備鏈表*/
    struct klist_node    knode_bus;

    struct module        * owner;
    const char         * mod_name;    /* used for built-in modules */
    struct module_kobject    * mkobj;

    int    (*probe)    (struct device * dev);/*查詢一個特定設備是否存在及驅動是否可以使用它的函數*/
    int    (*remove)    (struct device * dev);/*將設備從系統中刪除*/
    void    (*shutdown)    (struct device * dev);/*關閉設備*/
    int    (*suspend)    (struct device * dev, pm_message_t state);
    int    (*resume)    (struct device * dev);
};

/*註冊device_driver 結構的函數是:*/
int driver_register(struct device_driver *drv);
void driver_unregister(struct device_driver *drv);

/*driver的屬性結構在:*/
struct driver_attribute {
 struct attribute attr;
 ssize_t (*show)(struct device_driver *drv, char *buf);
 ssize_t (*store)(struct device_driver *drv, const char *buf, size_t count);
};
DRIVER_ATTR(_name,_mode,_show,_store)

/*屬性文件創建的方法:*/
int driver_create_file(struct device_driver * drv, struct driver_attribute * attr);
void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr);

/*bus_type 結構含有一個成員( drv_attrs ) 指向一組爲屬於該總線的所有設備創建的默認屬性*/

 

在更新的內核裏,這個結構體變得更簡潔了,隱藏了無需驅動編程人員知道的一些成員:

/*in Linux 2.6.26.5*/

struct device_driver {
    const char        *name;
    struct bus_type        *bus;

    struct module        *owner;
    const char         *mod_name;    /* used for built-in modules */

    int (*probe) (struct device *dev);
    int (*remove) (struct device *dev);
    void (*shutdown) (struct device *dev);
    int (*suspend) (struct device *dev, pm_message_t state);
    int (*resume) (struct device *dev);
    struct attribute_group **groups;

    struct driver_private *p;
};


struct driver_private {
    struct kobject kobj;
    struct klist klist_devices;
    struct klist_node knode_bus;
    struct module_kobject *mkobj;
    struct device_driver *driver;
};
#define to_driver(obj) container_of(obj, struct driver_private, kobj)

 

驅動程序結構的嵌入

對大多數驅動程序核心結構, device_driver 結構通常被嵌入到一個更高層的、總線相關的結構中。

以lddbus 子系統爲例,它定義了ldd_driver 結構:

struct ldd_driver {
 char *version;
 struct module *module;
 struct device_driver driver;
 struct driver_attribute version_attr; 
}; 
#define to_ldd_driver(drv) container_of(drv, struct ldd_driver, driver);

lddbus總線中相關的驅動註冊和註銷函數是:

/*
 * Crude driver interface.
 */
static ssize_t show_version(struct device_driver *driver, char *buf)
{
    struct ldd_driver *ldriver = to_ldd_driver(driver);
    sprintf(buf, "%s\n", ldriver->version);
    return strlen(buf);
}

int register_ldd_driver(struct ldd_driver *driver)
{
 int ret;
 driver->driver.bus = &ldd_bus_type;
 ret = driver_register(&driver->driver);/*註冊底層的 device_driver 結構到核心*/
 if (ret)
 return ret;
 driver->version_attr.attr.name = "version";/* driver_attribute 結構必須手工填充*/
 driver->version_attr.attr.owner = driver->module;/*注意:設定 version 屬性的擁有者爲驅動模塊, 不是 lddbus 模塊!因爲 show_version 函數是使用驅動模塊所創建的 ldd_driver 結構,若 ldd_driver 結構在一個用戶空間進程試圖讀取版本號時已經註銷,就會出錯*/
 driver->version_attr.attr.mode = S_IRUGO;
 driver->version_attr.show = show_version;
 driver->version_attr.store = NULL;
 return driver_create_file(&driver->driver, &driver->version_attr);/*建立版本屬性,因爲這個屬性在運行時被創建,所以不能使用 DRIVER_ATTR 宏*/
}

void unregister_ldd_driver(struct ldd_driver *driver)
{
    driver_unregister(&driver->driver);
}
EXPORT_SYMBOL(register_ldd_driver);
EXPORT_SYMBOL(unregister_ldd_driver);

在sculld 中創建的 ldd_driver 結構如下:

/* Device model stuff */
static struct ldd_driver sculld_driver = {
    .version = "$Revision: 1.21 $",
    .module = THIS_MODULE,
    .driver = {
        .name = "sculld",
    },
};/*只要一個簡單的 register_ldd_driver 調用就可添加它到系統中。一旦完成初始化, 驅動信息可在 sysfs 中顯示*/


 

類 子系統

類是一個設備的高層視圖, 它抽象出了底層的實現細節,從而允許用戶空間使用設備所提供的功能,而不用關心設備是如何連接和工作的。類成員通常由上層代碼所控制,而無需驅動的明確支持。但有些情況下驅動也需要直接處理類。

幾乎所有的類都顯示在 /sys/class 目錄中。出於歷史的原因,有一個例外:塊設備顯示在 /sys/block目錄中。在許多情況, 類子系統是向用戶空間導出信息的最好方法。當類子系統創建一個類時, 它將完全擁有這個類,根本不用擔心哪個模塊擁有那些屬性,而且信息的表示也比較友好。

爲了管理類,驅動程序核心導出了一些接口,其目的之一是提供包含設備號的屬性以便自動創建設備節點,所以udev的使用離不開類。 類函數和結構與設備模型的其他部分遵循相同的模式,所以真正嶄新的概念是很少的。

注意:class_simple 是老接口,在2.6.13中已被刪除,這裏不再研究。

管理類的接口

類由 struct class 的結構體來定義:

/*
 * device classes
 */
struct class {
    const char        * name;/*每個類需要一個唯一的名字, 它將顯示在 /sys/class 中*/
    struct module        * owner;

    struct kset        subsys;
    struct list_head    children;
    struct list_head    devices;
    struct list_head    interfaces;
    struct kset        class_dirs;
    struct semaphore    sem;    /* locks both the children and interfaces lists */

    struct class_attribute        * class_attrs;/* 指向類屬性的指針(以NULL結尾) */
    struct class_device_attribute    * class_dev_attrs;/* 指向類中每個設備的一組默認屬性的指針 */
    struct device_attribute        * dev_attrs;

    int    (*uevent)(struct class_device *dev, char **envp,
             int num_envp, char *buffer, int buffer_size);/* 類熱插拔產生時添加環境變量的函數 */
    int    (*dev_uevent)(struct device *dev, char **envp, int num_envp,
                char *buffer, int buffer_size);/* 類中的設備熱插拔時添加環境變量的函數 */

    void    (*release)(struct class_device *dev);/* 把設備從類中刪除的函數 */
    void    (*class_release)(struct class *class);/* 刪除類本身的函數 */
    void    (*dev_release)(struct device *dev);

    int    (*suspend)(struct device *, pm_message_t state);
    int    (*resume)(struct device *);
};


/*類註冊函數:*/
int class_register(struct class *cls);
void class_unregister(struct class *cls);

/*類屬性的接口:*/
struct class_attribute {
 struct attribute attr;
 ssize_t (*show)(struct class *cls, char *buf);
 ssize_t (*store)(struct class *cls, const char *buf, size_t count); 
}; 
CLASS_ATTR(_name,_mode,_show,_store); 
int class_create_file(struct class *cls, const struct class_attribute *attr);
void class_remove_file(struct class *cls, const struct class_attribute *attr);

在更新的內核裏,這個結構體變得簡潔了,刪除了一些成員:

/*in Linux 2.6.26.5*/

/*
 * device classes
 */
struct class {
    const char        *name;
    struct module        *owner;

    struct kset        subsys;
    struct list_head    devices;
    struct list_head    interfaces;
    struct kset        class_dirs;
    struct semaphore    sem; /* locks children, devices, interfaces */
    struct class_attribute        *class_attrs;
    struct device_attribute        *dev_attrs;

    int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);

    void (*class_release)(struct class *class);
    void (*dev_release)(struct device *dev);

    int (*suspend)(struct device *dev, pm_message_t state);
    int (*resume)(struct device *dev);
};

類設備(在新內核中已被刪除)

類存在的真正目的是給作爲類成員的各個設備提供一個容器,成員由 struct class_device 來表示:

struct class_device {
    struct list_head    node;/*for internal use by the driver core only*/
    struct kobject        kobj;/*for internal use by the driver core only*/
    struct class        * class;    /* 指向該設備所屬的類,必須*/
    dev_t            devt;        /* dev_t, creates the sysfs "dev" ,for internal use by the driver core only*/
    struct class_device_attribute *devt_attr;/*for internal use by the driver core only*/
    struct class_device_attribute uevent_attr;
    struct device        * dev;        /* 指向此設備相關的 device 結構體,可選。若不爲NULL,應是一個從類入口到/sys/devices 下相應入口的符號連接,以便用戶空間查找設備入口*/
    void            * class_data;    /* 私有數據指針 */
    struct class_device    *parent;    /* parent of this child device, if there is one */
    struct attribute_group ** groups;    /* optional groups */

    void    (*release)(struct class_device *dev);
    int    (*uevent)(struct class_device *dev, char **envp,
             int num_envp, char *buffer, int buffer_size);
    char    class_id[BUS_ID_SIZE];    /* 此類中的唯一的名字 */
};

/*類設備註冊函數:*/
int class_device_register(struct class_device *cd);
void class_device_unregister(struct class_device *cd);

/*重命名一個已經註冊的類設備入口:*/
int class_device_rename(struct class_device *cd, char *new_name); 

/*類設備入口屬性:*/
struct class_device_attribute {
 struct attribute attr;
 ssize_t (*show)(struct class_device *cls, char *buf);
 ssize_t (*store)(struct class_device *cls, const char *buf,
 size_t count);
};

CLASS_DEVICE_ATTR(_name, _mode, _show, _store); 

/*創建和刪除除struct class中設備默認屬性外的屬性*/
int class_device_create_file(struct class_device *cls, const struct class_device_attribute *attr);
void class_device_remove_file(struct class_device *cls, const struct class_device_attribute *attr);

類接口

類子系統有一個 Linux 設備模型的其他部分找不到的附加概念,稱爲“接口”, 可將它理解爲一種設備加入或離開類時獲得信息的觸發機制,結構體如下:

struct class_interface {
    struct list_head    node;
    struct class        *class;/* 指向該接口所屬的類*/

    int (*add) (struct class_device *, struct class_interface *);

 /*當一個類設備被加入到在 class_interface 結構中指定的類時, 將調用接口的 add 函數,進行一些設備需要的額外設置,通常是添加更多屬性或其他的一些工作*/
    void (*remove)    (struct class_device *, struct class_interface *);/*一個接口的功能是簡單明瞭的. 當設備從類中刪除, 將調用remove 方法來進行必要的清理*/
    int (*add_dev)     (struct device *, struct class_interface *);
    void (*remove_dev) (struct device *, struct class_interface *);
};

/*註冊或註銷接口的函數:*/
int class_interface_register(struct class_interface *class_intf);
void class_interface_unregister(struct class_interface *class_intf);
/*一個類可註冊多個接口*/

 

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