日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

設(shè)備模型(總線、設(shè)備、驅(qū)動(dòng)程序和類)

 WUCANADA 2012-06-10


Linux設(shè)備驅(qū)動(dòng)程序?qū)W習(xí)(13)

-Linux設(shè)備模型(總線、設(shè)備、驅(qū)動(dòng)程序和類)


文章的例子和實(shí)驗(yàn)使用《LDD3》所配的lddbus模塊(稍作修改)。

提示:在學(xué)習(xí)這部分內(nèi)容是一定要分析所有介紹的源代碼,知道他們與上一部分內(nèi)容(kobject、kset、attribute等等)的關(guān)系,最好要分析一個(gè)實(shí)際的“flatform device”設(shè)備,不然會(huì)只學(xué)到表象,到后面會(huì)不知所云的。

總線

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

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

    struct kset        subsys;/*與該總線相關(guān)的子系統(tǒng)*/
    struct kset        drivers;/*總線驅(qū)動(dòng)程序的kset*/
    struct kset        devices;/* 掛在該總線的所有設(shè)備的kset*/

    struct klist        klist_devices;/*與該總線相關(guān)的驅(qū)動(dòng)程序鏈表*/
    struct klist        klist_drivers;/*掛接在該總線的設(shè)備鏈表*/

    struct blocking_notifier_head bus_notifier;

    struct bus_attribute    * bus_attrs; /*總線屬性*/
    struct device_attribute * dev_attrs; /*設(shè)備屬性,指向?yàn)槊總€(gè)加入總線的設(shè)備建立的默認(rèn)屬性鏈表*/
    struct driver_attribute * drv_attrs; /*驅(qū)動(dòng)程序?qū)傩?/
    struct bus_attribute drivers_autoprobe_attr;/*驅(qū)動(dòng)自動(dòng)探測(cè)屬性*/
    struct bus_attribute drivers_probe_attr;/*驅(qū)動(dòng)探測(cè)屬性*/

    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);
/*處理熱插拔、電源管理、探測(cè)和移除等事件的方法*/
    unsigned int drivers_autoprobe:1;
};


在更新的內(nèi)核里,這個(gè)結(jié)構(gòu)體變得更簡潔了,隱藏了無需驅(qū)動(dòng)編程人員知道的一些成員:

 

/*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;
};

總線的注冊(cè)和刪除

總線的主要注冊(cè)步驟:

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

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

(2)調(diào)用bus_register函數(shù)注冊(cè)總線。

int bus_register(struct bus_type * bus)

調(diào)用可能失敗, 所以必須始終檢查返回值。若成功,新的總線子系統(tǒng)將被添加進(jìn)系統(tǒng),并可在 sysfs 的 /sys/bus 下看到。之后可以向總線添加設(shè)備。
例如:

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

 
當(dāng)必須從系統(tǒng)中刪除一個(gè)總線時(shí), 調(diào)用:

void bus_unregister(struct bus_type *bus);

總線方法

在 bus_type 結(jié)構(gòu)中定義了許多方法,它們?cè)试S總線核心作為設(shè)備核心和單獨(dú)的驅(qū)動(dòng)程序之間提供服務(wù)的中介,主要介紹以下兩個(gè)方法:

int (*match)(struct device * dev, struct device_driver * drv);
/*當(dāng)一個(gè)新設(shè)備或者驅(qū)動(dòng)被添加到這個(gè)總線時(shí),這個(gè)方法會(huì)被調(diào)用一次或多次,若指定的驅(qū)動(dòng)程序能夠處理指定的設(shè)備,則返回非零值。必須在總線層使用這個(gè)函數(shù), 因?yàn)槟抢锎嬖谡_的邏輯,核心內(nèi)核不知道如何為每個(gè)總線類型匹配設(shè)備和驅(qū)動(dòng)程序*/

int (*uevent)(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size);
/*在為用戶空間產(chǎn)生熱插拔事件之前,這個(gè)方法允許總線添加環(huán)境變量(參數(shù)和 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));
}/*僅簡單比較驅(qū)動(dòng)和設(shè)備的名字*/
/*當(dāng)涉及實(shí)際硬件時(shí), match 函數(shù)常常對(duì)設(shè)備提供的硬件 ID 和驅(qū)動(dòng)所支持的 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;
}/*在環(huán)境變量中加入 lddbus 源碼的當(dāng)前版本號(hào)*/

對(duì)設(shè)備和驅(qū)動(dòng)的迭代

若要編寫總線層代碼, 可能不得不對(duì)所有已經(jīng)注冊(cè)到總線的設(shè)備或驅(qū)動(dòng)進(jìn)行一些操作,這可能需要仔細(xì)研究嵌入到 bus_type 結(jié)構(gòu)中的其他數(shù)據(jù)結(jié)構(gòu), 但最好使用內(nèi)核提供的輔助函數(shù):

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 *));

/* 這兩個(gè)函數(shù)迭代總線上的每個(gè)設(shè)備或驅(qū)動(dòng)程序, 將關(guān)聯(lián)的 device 或 device_driver 傳遞給 fn, 同時(shí)傳遞 data 值。若 start 為 NULL, 則從第一個(gè)設(shè)備開始; 否則從 start 之后的第一個(gè)設(shè)備開始。若 fn 返回非零值, 迭代停止并且那個(gè)值從 bus_for_each_dev 或bus_for_each_drv 返回。*/

總線屬性

幾乎 Linux 設(shè)備模型中的每一層都提供添加屬性的函數(shù), 總線層也不例外。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 很相似,其實(shí)大部分在 kobject 級(jí)上的設(shè)備模型層都是以這種方式工作。

內(nèi)核提供了一個(gè)宏在編譯時(shí)創(chuàng)建和初始化 bus_attribute 結(jié)構(gòu):

BUS_ATTR(_name,_mode,_show,_store)/*這個(gè)宏聲明一個(gè)結(jié)構(gòu), 將 bus_attr_ 作為給定 _name 的前綴來創(chuàng)建總線的真正名稱*/

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

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

例如創(chuàng)建一個(gè)包含源碼版本號(hào)簡單屬性文件方法如下:

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);

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

/*這個(gè)調(diào)用創(chuàng)建一個(gè)包含 lddbus 代碼的版本號(hào)的屬性文件(/sys/bus/ldd/version)*/


設(shè)備

在最底層, Linux 系統(tǒng)中的每個(gè)設(shè)備由一個(gè) 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;/* 設(shè)備的 "父" 設(shè)備,該設(shè)備所屬的設(shè)備,通常一個(gè)父設(shè)備是某種總線或者主控制器. 如果 parent 是 NULL, 則該設(shè)備是頂層設(shè)備,較少見 */

    struct kobject kobj;/*代表該設(shè)備并將其連接到結(jié)構(gòu)體系中的 kobject; 注意:作為通用的規(guī)則, device->kobj->parent 應(yīng)等于 device->parent->kobj*/
    char    bus_id[BUS_ID_SIZE];/*在總線上唯一標(biāo)識(shí)該設(shè)備的字符串;例如: PCI 設(shè)備使用標(biāo)準(zhǔn)的 PCI ID 格式, 包含:域, 總線, 設(shè)備, 和功能號(hào).*/
    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;     /*標(biāo)識(shí)該設(shè)備連接在何種類型的總線上*/
    struct device_driver *driver;    /*管理該設(shè)備的驅(qū)動(dòng)程序*/
    void        *driver_data;    /*該設(shè)備驅(qū)動(dòng)使用的私有數(shù)據(jù)成員*/
    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);/*當(dāng)這個(gè)設(shè)備的最后引用被刪除時(shí),內(nèi)核調(diào)用該方法; 它從被嵌入的 kobject 的 release 方法中調(diào)用。所有注冊(cè)到核心的設(shè)備結(jié)構(gòu)必須有一個(gè) release 方法, 否則內(nèi)核將打印錯(cuò)誤信息*/
};
/*在注冊(cè) struct device 前,最少要設(shè)置parent, bus_id, bus, 和 release 成員*/

設(shè)備注冊(cè)

設(shè)備的注冊(cè)和注銷函數(shù)為:

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

一個(gè)實(shí)際的總線也是一個(gè)設(shè)備,所以必須單獨(dú)注冊(cè),以下為 lddbus 在編譯時(shí)注冊(cè)它的虛擬總線設(shè)備源碼:

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*/

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

設(shè)備屬性

sysfs 中的設(shè)備入口可有屬性,相關(guān)的結(jié)構(gòu)是:

/* interface for exporting device attributes 這個(gè)結(jié)構(gòu)體和《LDD3》中的不同,已經(jīng)被更新過了,請(qǐng)?zhí)貏e注意!*/
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);
};

/*設(shè)備屬性結(jié)構(gòu)可在編譯時(shí)建立, 使用以下宏:*/
DEVICE_ATTR(_name,_mode,_show,_store);
/*這個(gè)宏聲明一個(gè)結(jié)構(gòu), 將 dev_attr_ 作為給定 _name 的前綴來命名設(shè)備屬性

/*屬性文件的實(shí)際處理使用以下函數(shù):*/

 int device_create_file(struct device *device,    struct device_attribute * entry);
 void device_remove_file(struct device * dev, struct device_attribute * attr);

設(shè)備結(jié)構(gòu)的嵌入

device 結(jié)構(gòu)包含設(shè)備模型核心用來模擬系統(tǒng)的信息。但大部分子系統(tǒng)記錄了關(guān)于它們又擁有的設(shè)備的額外信息,所以很少單純用 device 結(jié)構(gòu)代表設(shè)備,而是,通常將其嵌入一個(gè)設(shè)備的高層表示中。底層驅(qū)動(dòng)幾乎不知道 struct device。

lddbus 驅(qū)動(dòng)創(chuàng)建了它自己的 device 類型,并期望每個(gè)設(shè)備驅(qū)動(dòng)使用這個(gè)類型來注冊(cè)它們的設(shè)備:

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 導(dǎo)出的注冊(cè)和注銷接口如下:

/*
 * 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 驅(qū)動(dòng)添加一個(gè)自己的屬性到它的設(shè)備入口,稱為 dev, 僅包含關(guān)聯(lián)的設(shè)備號(hào),源碼如下:

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);

/*接著, 在初始化時(shí)間, 設(shè)備被注冊(cè), 并且 dev 屬性通過下面的函數(shù)被創(chuàng)建:*/
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 成員來存儲(chǔ)指向我們自己的內(nèi)部的設(shè)備結(jié)構(gòu)的指針。請(qǐng)檢查
device_create_file的返回值,否則編譯時(shí)會(huì)有警告。*/


設(shè)備驅(qū)動(dòng)程序

設(shè)備模型跟蹤所有系統(tǒng)已知的驅(qū)動(dòng),主要目的是使驅(qū)動(dòng)程序核心能協(xié)調(diào)驅(qū)動(dòng)和新設(shè)備之間的關(guān)系。一旦驅(qū)動(dòng)在系統(tǒng)中是已知的對(duì)象就可能完成大量的工作。驅(qū)動(dòng)程序的結(jié)構(gòu)體 device_driver 定義如下:

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

    struct kobject        kobj;/*內(nèi)嵌的kobject對(duì)象*/
    struct klist        klist_devices;/*當(dāng)前驅(qū)動(dòng)程序能操作的設(shè)備鏈表*/
    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);/*查詢一個(gè)特定設(shè)備是否存在及驅(qū)動(dòng)是否可以使用它的函數(shù)*/
    int    (*remove)    (struct device * dev);/*將設(shè)備從系統(tǒng)中刪除*/
    void    (*shutdown)    (struct device * dev);/*關(guān)閉設(shè)備*/
    int    (*suspend)    (struct device * dev, pm_message_t state);
    int    (*resume)    (struct device * dev);
};

/*注冊(cè)device_driver 結(jié)構(gòu)的函數(shù)是:*/
int driver_register(struct device_driver *drv);
void driver_unregister(struct device_driver *drv);

/*driver的屬性結(jié)構(gòu)在:*/
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)

/*屬性文件創(chuàng)建的方法:*/
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 結(jié)構(gòu)含有一個(gè)成員( drv_attrs ) 指向一組為屬于該總線的所有設(shè)備創(chuàng)建的默認(rèn)屬性*/


在更新的內(nèi)核里,這個(gè)結(jié)構(gòu)體變得更簡潔了,隱藏了無需驅(qū)動(dòng)編程人員知道的一些成員:

/*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)


驅(qū)動(dòng)程序結(jié)構(gòu)的嵌入

對(duì)大多數(shù)驅(qū)動(dòng)程序核心結(jié)構(gòu), device_driver 結(jié)構(gòu)通常被嵌入到一個(gè)更高層的、總線相關(guān)的結(jié)構(gòu)中。

以lddbus 子系統(tǒng)為例,它定義了ldd_driver 結(jié)構(gòu):

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總線中相關(guān)的驅(qū)動(dòng)注冊(cè)和注銷函數(shù)是:

/*
 * 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);/*注冊(cè)底層的 device_driver 結(jié)構(gòu)到核心*/
 if (ret)
 return ret;
 driver->version_attr.attr.name = "version";/* driver_attribute 結(jié)構(gòu)必須手工填充*/
 driver->version_attr.attr.owner = driver->module;/* 注意:設(shè)定 version 屬性的擁有者為驅(qū)動(dòng)模塊, 不是 lddbus 模塊!因?yàn)?show_version 函數(shù)是使用驅(qū)動(dòng)模塊所創(chuàng)建的 ldd_driver 結(jié)構(gòu),若 ldd_driver 結(jié)構(gòu)在一個(gè)用戶空間進(jìn)程試圖讀取版本號(hào)時(shí)已經(jīng)注銷,就會(huì)出錯(cuò)*/
 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);/*建立版本屬性,因?yàn)檫@個(gè)屬性在運(yùn)行時(shí)被創(chuàng)建,所以不能使用 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 中創(chuàng)建的 ldd_driver 結(jié)構(gòu)如下:

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


類 子系統(tǒng)

類是一個(gè)設(shè)備的高層視圖, 它抽象出了底層的實(shí)現(xiàn)細(xì)節(jié),從而允許用戶空間使用設(shè)備所提供的功能, 而不用關(guān)心設(shè)備是如何連接和工作的。類成員通常由上層代碼所控制, 而無需驅(qū)動(dòng)的明確支持。但有些情況下驅(qū)動(dòng)也需要直接處理類。

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

為了管理類,驅(qū)動(dòng)程序核心導(dǎo)出了一些接口,其目的之一是提供包含設(shè)備號(hào)的屬性以便自動(dòng)創(chuàng)建設(shè)備節(jié)點(diǎn),所以u(píng)dev的使用離不開類。 類函數(shù)和結(jié)構(gòu)與設(shè)備模型的其他部分遵循相同的模式,所以真正嶄新的概念是很少的。

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

管理類的接口

類由 struct class 的結(jié)構(gòu)體來定義:

/*
 * device classes
 */

struct class {
    const char        * name;/*每個(gè)類需要一個(gè)唯一的名字, 它將顯示在 /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結(jié)尾) */
    struct class_device_attribute    * class_dev_attrs;/* 指向類中每個(gè)設(shè)備的一組默認(rèn)屬性的指針 */
    struct device_attribute        * dev_attrs;

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

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

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


/*類注冊(cè)函數(shù):*/
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);


在更新的內(nèi)核里,這個(gè)結(jié)構(gòu)體變得簡潔了,刪除了一些成員:

/*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);
};

類設(shè)備(在新內(nèi)核中已被刪除)

類存在的真正目的是給作為類成員的各個(gè)設(shè)備提供一個(gè)容器,成員由 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;    /* 指向該設(shè)備所屬的類,必須*/
    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;        /* 指向此設(shè)備相關(guān)的 device 結(jié)構(gòu)體,可選。若不為NULL,應(yīng)是一個(gè)從類入口到/sys/devices 下相應(yīng)入口的符號(hào)連接,以便用戶空間查找設(shè)備入口*/
    
void            * class_data;    /* 私有數(shù)據(jù)指針 */
    
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];    /* 此類中的唯一的名字 */
};

/*類設(shè)備注冊(cè)函數(shù):*/
int class_device_register(struct class_device *cd);
void class_device_unregister(struct class_device *cd);

/*重命名一個(gè)已經(jīng)注冊(cè)的類設(shè)備入口:*/
int class_device_rename(struct class_device *cd, char *new_name);

/*類設(shè)備入口屬性:*/
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);

/*創(chuàng)建和刪除除struct class中設(shè)備默認(rèn)屬性外的屬性*/
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);

類接口

類子系統(tǒng)有一個(gè) Linux 設(shè)備模型的其他部分找不到的附加概念,稱為“接口”, 可將它理解為一種設(shè)備加入或離開類時(shí)獲得信息的觸發(fā)機(jī)制,結(jié)構(gòu)體如下:

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

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

/*當(dāng)一個(gè)類設(shè)備被加入到在 class_interface 結(jié)構(gòu)中指定的類時(shí), 將調(diào)用接口的 add 函數(shù),進(jìn)行一些設(shè)備需要的額外設(shè)置,通常是添加更多屬性或其他的一些工作*/
    void (*remove)    (struct class_device *, struct class_interface *);/*一個(gè)接口的功能是簡單明了的. 當(dāng)設(shè)備從類中刪除, 將調(diào)用remove 方法來進(jìn)行必要的清理*/
    int (*add_dev)     (struct device *, struct class_interface *);
    void (*remove_dev) (struct device *, struct class_interface *);
};

/*注冊(cè)或注銷接口的函數(shù):*/
int class_interface_register(struct class_interface *class_intf);
void class_interface_unregister(struct class_interface *class_intf);
/*一個(gè)類可注冊(cè)多個(gè)接口*/


    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多