1 匯流排
匯流排,是處理器與一個或者多個裝置之間的通道。在Linux裝置模型中,所有的裝置都通過匯流排相連,甚至是那些內部的虛擬"platform"匯流排。用bus_type結構來描述。
struct bus_type {const char*name; /* 匯流排名 */const char*dev_name; struct device*dev_root;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 (*resume)(struct device *dev); /* 裝置、驅動的電源管理操作集 */const struct dev_pm_ops *pm;struct iommu_ops *iommu_ops; /* 私人資料指標,這個比較重要的一個成員 */struct subsys_private *p;};
2. 匯流排聲明
核心中的每一種匯流排類型(PCI,I2C,USB,etc)都用bus_type結構體來描述,需要初始化相應的name成員和match函數。
struct bus_type pci_bus_type = { .name = "pci", .match = pci_bus_match, };
3. 註冊和登出函數
int bus_register(struct bus_type *bus);
void bus_unregister(struct bus_type *bus);
匯流排 註冊函數分析
int bus_register(struct bus_type *bus){ retval =kobject_set_name(&priv->subsys.kobj, "%s", bus->name); priv->subsys.kobj.kset = bus_kset; priv->subsys.kobj.ktype =&bus_ktype; priv->drivers_autoprobe = 1; /* 註冊bus kset,在bus/下產生bus->name目錄 */ retval = kset_register(&priv->subsys); retval = bus_create_file(bus,&bus_attr_uevent); /* 產生device目錄 */ priv->devices_kset =kset_create_and_add("devices", NULL, &priv->subsys.kobj); /* 產生driver目錄 */ priv->drivers_kset =kset_create_and_add("drivers", NULL, &priv->subsys.kobj); /* 初始化裝置鏈和驅動鏈 */ klist_init(&priv->klist_devices,klist_devices_get, klist_devices_put); klist_init(&priv->klist_drivers,NULL, NULL); retval = add_probe_files(bus); retval = bus_add_attrs(bus);}
Klist_drivers和klist_devices這兩條鏈非常重要,用來遍曆drivers_kset和devices_kset,drivers_kset和devices_kset分別是匯流排上所有driver和device的集合。
4. match() 函數
match函數為匯流排提供了檢測驅動是否支援此裝置的功能,通過device IDs來進行匹配。當驅動被註冊到匯流排上,匯流排上的裝置鏈表會被遍曆,match()被調用來完成對每個裝置的匹配。
5. subsys_private
subsys_private是一個比較重要的成員。
struct subsys_private {struct kset subsys;struct kset *devices_kset; /* 裝置集,包含匯流排上所有的裝置 */struct list_head interfaces;struct mutex mutex;struct kset *drivers_kset; /* 驅動集,包含匯流排所有的驅動 */struct klist klist_devices; /* 裝置鏈表,串連匯流排上所有的裝置 */struct klist klist_drivers; /* 驅動鏈表,串連匯流排上所有的驅動 */struct blocking_notifier_head bus_notifier;unsigned int drivers_autoprobe:1;struct bus_type *bus; /* 指回bus_type */struct kset glue_dirs;struct class *class;};
裝置鏈表和驅動鏈表串連了註冊到匯流排上的所有裝置和驅動,經常用來遍曆和查詢匯流排上的裝置和驅動。裝置模型核心層提供了兩個API,用來遍曆裝置和驅動。
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 *));
6. 匯出屬性,匯流排的屬性用bus_attribute來描述
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); /* 寫屬性方法 */};
BUS_ATTR宏可以在編譯時間刻建立和初始化bus_attribute結構體
BUS_ATTR(_name, _mode, _show, _store)
在LDM裡提供了兩個介面用來在sysfs目錄下產生和移除屬性檔案
int bus_create_file(struct bus_type *, struct bus_attribute *);void bus_remove_file(struct bus_type *, struct bus_attribute *);
7. 執行個體分析
建立一條scbus匯流排,並添加版本屬性。實際中我們並不需要自己建立匯流排,此實驗僅用來學習
/** for learn bus*/#include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/device.h> static char *Version = "revision 1.0,scbus"; /* 匹配函數,通過裝置名稱和驅動名來匹配 */static int scbus_match(struct device *dev,struct device_driver *driver){ printk("\n%s,%s\n", dev_name(dev), driver->name); return!strncmp(dev_name(dev), driver->name, strlen(driver->name));} static void scbus_release(struct device*dev){ printk("scbusrelease\n");} struct bus_type scbus_type = { .name = "scbus", .match = scbus_match,};EXPORT_SYMBOL_GPL(scbus_type); struct device scbus = { .init_name = "scbus0", .release = scbus_release,};EXPORT_SYMBOL_GPL(scbus); /** export bus attribute*/static ssize_t show_bus_version(structbus_type *bus, char *buf){ returnsnprintf(buf, PAGE_SIZE, "%s\n", Version);}static BUS_ATTR(version, S_IRUGO,show_bus_version, NULL); static int __init scbus_init(void){ intret; ret= bus_register(&scbus_type); if(ret) returnret; ret= bus_create_file(&scbus_type, &bus_attr_version); if(ret) gotocreate_error; ret= device_register(&scbus); if(ret) gotodevice_error; printk("Createa scbus\n"); return0; device_error: bus_remove_file(&scbus_type,&bus_attr_version);create_error: bus_unregister(&scbus_type); returnret;} static void __exit scbus_exit(void){ device_unregister(&scbus); bus_remove_file(&scbus_type,&bus_attr_version); bus_unregister(&scbus_type); printk("Removea scbus\n");} module_init(scbus_init);module_exit(scbus_exit); MODULE_LICENSE("Dual BSD/GPL");MODULE_AUTHOR("CJOK<cjok.liao@gmail.com>");
實驗結果: