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è)接口*/
|
|