前面我們分析了設備驅動模型中的device和driver,device和driver本來是不相關的東西,只因為bus的存在,才被聯系到了一起。本節就來看看設備驅動模型中起樞紐作用的bus。本節的頭文件在include/linux/device.h和drivers/base/base.h,實現代碼主要在bus.c中。因為在bus中有很多代碼時為了device找到driver或者driver找到device而定義的,本節先盡量忽略這部分,專注于bus的注冊和注銷,屬性定義等內容。剩下的留到討論device和driver關系時在分析。
先來看看bus的數據結構。
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?(*resume)(struct?device?*dev);?? ?? ????const?struct?dev_pm_ops?*pm;?? ?? ????struct?bus_type_private?*p;?? };??
struct bus_type是bus的通用數據結構。
name是bus的名稱,注意到這里也是const char類型的,在sysfs中使用的還是kobj中動態創建的名稱,這里的name只是初始名。
bus_attrs是bus為自己定義的一系列屬性,dev_attrs是bus為旗下的device定義的一系列屬性,drv_attrs是bus為旗下的driver定義的一系列屬性。其中dev_attrs在bus_add_device()->device_add_attrs()中被加入dev目錄下,drv_attrs在bus_add_driver()->driver_add_attrs()中被加入driver目錄下。
match函數匹配總線中的dev和driver,返回值為1代表匹配成功,為0則失敗。
uevent函數用于總線對uevent的環境變量添加,但在總線下設備的dev_uevent處理函數也有對它的調用。
probe函數是總線在匹配成功時調用的函數,bus->probe和drv->probe中只會有一個起效,同時存在時使用bus->probe。
remove函數在總線上設備或者驅動要刪除時調用,bus->remove和drv->remove中同樣只會有一個起效。
shutdown函數在所有設備都關閉時調用,即在core.c中的device_shutdown()函數中調用,bus->shutdown和drv->shutdown同樣只會有一個起效。
suspend函數是在總線上設備休眠時調用。
resume函數是在總線上設備恢復時調用。
pm是struct dev_pm_ops類型,其中定義了一系列電源管理的函數。
p是指向bus_type_private的指針,其中定義了將bus同其它組件聯系起來的變量。
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;?? };?? ?? #define?to_bus(obj)?container_of(obj,?struct?bus_type_private,?subsys.kobj)??
struct bus_type_private是將bus同device、driver、sysfs聯系起來的結構。
subsys是kset類型,代表bus在sysfs中的類型。
drivers_kset代表bus目錄下的drivers子目錄。
devices_kset代表bus目錄下地devices子目錄。
klist_devices是bus的設備鏈表,klist_drivers是bus的驅動鏈表。
bus_notifier用于在總線上內容發送變化時調用特定的函數,這里略過。
driver_autoprobe標志定義是否允許device和driver自動匹配,如果允許會在device或者driver注冊時就進行匹配工作。
bus指針指向struct bus_type類型。
使用struct bus_type_private可以將struct bus_type中的部分細節屏蔽掉,利于外界使用bus_type。struct? driver_private和struct device_private都有類似的功能。
struct?bus_attribute?{?? ????struct?attribute????attr;?? ????ssize_t?(*show)(struct?bus_type?*bus,?char?*buf);?? ????ssize_t?(*store)(struct?bus_type?*bus,?const?char?*buf,?size_t?count);?? };?? ?? #define?BUS_ATTR(_name,?_mode,?_show,?_store)???\??? struct?bus_attribute?bus_attr_##_name?=?__ATTR(_name,?_mode,?_show,?_store)?? ?? #define?to_bus_attr(_attr)?container_of(_attr,?struct?bus_attribute,?attr)??
struct bus_attribute是bus對struct attribute類型的封裝,更方便總線屬性的定義。
static?ssize_t?bus_attr_show(struct?kobject?*kobj,?struct?attribute?*attr,?? ?????????????????char?*buf)?? {?? ????struct?bus_attribute?*bus_attr?=?to_bus_attr(attr);?? ????struct?bus_type_private?*bus_priv?=?to_bus(kobj);?? ????ssize_t?ret?=?0;?? ?? ????if?(bus_attr->show)?? ????????ret?=?bus_attr->show(bus_priv->bus,?buf);?? ????return?ret;?? }?? ?? static?ssize_t?bus_attr_store(struct?kobject?*kobj,?struct?attribute?*attr,?? ??????????????????const?char?*buf,?size_t?count)?? {?? ????struct?bus_attribute?*bus_attr?=?to_bus_attr(attr);?? ????struct?bus_type_private?*bus_priv?=?to_bus(kobj);?? ????ssize_t?ret?=?0;?? ?? ????if?(bus_attr->store)?? ????????ret?=?bus_attr->store(bus_priv->bus,?buf,?count);?? ????return?ret;?? }?? ?? static?struct?sysfs_ops?bus_sysfs_ops?=?{?? ????.show???=?bus_attr_show,?? ????.store??=?bus_attr_store,?? };?? ?? static?struct?kobj_type?bus_ktype?=?{?? ????.sysfs_ops??=?&bus_sysfs_ops,?? };??
以上應該是我們最熟悉的部分,bus_ktype中定義了bus對應的kset應該使用的kobj_type實例。與此類似,driver使用的是自定義的driver_ktype,device使用的是自定義的device_ktype。只是這里僅僅定義了sysfs_ops,并未定義release函數,不知bus_type_private打算何時釋放。
int?bus_create_file(struct?bus_type?*bus,?struct?bus_attribute?*attr)?? {?? ????int?error;?? ????if?(bus_get(bus))?{?? ????????error?=?sysfs_create_file(&bus->p->subsys.kobj,?&attr->attr);?? ????????bus_put(bus);?? ????}?else?? ????????error?=?-EINVAL;?? ????return?error;?? }?? ?? void?bus_remove_file(struct?bus_type?*bus,?struct?bus_attribute?*attr)?? {?? ????if?(bus_get(bus))?{?? ????????sysfs_remove_file(&bus->p->subsys.kobj,?&attr->attr);?? ????????bus_put(bus);?? ????}?? }??
bus_create_file()在bus目錄下創建屬性文件,bus_remove_file()在bus目錄下刪除屬性文件。類似的函數在driver和device中都有見到。
static?int?bus_uevent_filter(struct?kset?*kset,?struct?kobject?*kobj)?? {?? ????struct?kobj_type?*ktype?=?get_ktype(kobj);?? ?? ????if?(ktype?==?&bus_ktype)?? ????????return?1;?? ????return?0;?? }?? ?? static?struct?kset_uevent_ops?bus_uevent_ops?=?{?? ????.filter?=?bus_uevent_filter,?? };?? ?? static?struct?kset?*bus_kset;??
可以看到這里定義了一個bus_uevent_ops變量,這是kset對uevent事件處理所用的結構,它會用在bus_kset中。
int?__init?buses_init(void)?? {?? ????bus_kset?=?kset_create_and_add("bus",?&bus_uevent_ops,?NULL);?? ????if?(!bus_kset)?? ????????return?-ENOMEM;?? ????return?0;?? }??
在buses_init()中創建了/sys/bus目錄,這是一個kset類型,使用了bus_uevent_ops的uevent操作類型。
其實這里的操作不難想象,在devices中我們有一個類似的devices_kset,可以回顧一下。
static?struct?kset_uevent_ops?device_uevent_ops?=?{?? ????.filter?=???dev_uevent_filter,?? ????.name?=?????dev_uevent_name,?? ????.uevent?=???dev_uevent,?? };?? ?? ?? struct?kset?*devices_kset;?? ?? int?__init?devices_init(void)?? {?? ????devices_kset?=?kset_create_and_add("devices",?&device_uevent_ops,?NULL);?? ????????????????...?? }?? ?? void?device_initialize(struct?device?*dev)?? {?? ????dev->kobj.kset?=?devices_kset;?? ????????????????...?? }??
devices_kset在devices_init()中被創建,使用相應的device_uevent_ops進行uevent處理。而devices_kset又被設為每個device初始化時使用的kset。這就不難想象每個device都是以devices_kset為所屬kset的,并使用device_uevent_ops中的處理函數。
只是這里還不知bus_kset會在哪里用到,或許是每個bus所屬的kset吧,下面會有答案。
static?ssize_t?show_drivers_autoprobe(struct?bus_type?*bus,?char?*buf)?? {?? ????return?sprintf(buf,?"%d\n",?bus->p->drivers_autoprobe);?? }?? ?? static?ssize_t?store_drivers_autoprobe(struct?bus_type?*bus,?? ???????????????????????const?char?*buf,?size_t?count)?? {?? ????if?(buf[0]?==?'0')?? ????????bus->p->drivers_autoprobe?=?0;?? ????else?? ????????bus->p->drivers_autoprobe?=?1;?? ????return?count;?? }?? ?? static?ssize_t?store_drivers_probe(struct?bus_type?*bus,?? ???????????????????const?char?*buf,?size_t?count)?? {?? ????struct?device?*dev;?? ?? ????dev?=?bus_find_device_by_name(bus,?NULL,?buf);?? ????if?(!dev)?? ????????return?-ENODEV;?? ????if?(bus_rescan_devices_helper(dev,?NULL)?!=?0)?? ????????return?-EINVAL;?? ????return?count;?? }?? ?? static?BUS_ATTR(drivers_probe,?S_IWUSR,?NULL,?store_drivers_probe);?? static?BUS_ATTR(drivers_autoprobe,?S_IWUSR?|?S_IRUGO,?? ????????show_drivers_autoprobe,?store_drivers_autoprobe);??
這里定義了總線下的兩個屬性,只寫得drivers_probe,和可讀寫的drivers_autoprobe。至于其怎么實現的,我們現在還不關心。
static?int?add_probe_files(struct?bus_type?*bus)?? {?? ????int?retval;?? ?? ????retval?=?bus_create_file(bus,?&bus_attr_drivers_probe);?? ????if?(retval)?? ????????goto?out;?? ?? ????retval?=?bus_create_file(bus,?&bus_attr_drivers_autoprobe);?? ????if?(retval)?? ????????bus_remove_file(bus,?&bus_attr_drivers_probe);?? out:?? ????return?retval;?? }?? ?? static?void?remove_probe_files(struct?bus_type?*bus)?? {?? ????bus_remove_file(bus,?&bus_attr_drivers_autoprobe);?? ????bus_remove_file(bus,?&bus_attr_drivers_probe);?? }??
add_probe_files()在bus目錄下添加drivers_probe和drivers_autoprobe文件。
remove_probe_files()在bus目錄下刪除drivers_probe和drivers_autoprobe文件。
這兩個函數對bus的probe類型屬性進行管理,就像add_bind_files/remove_bind_files對driver的bind類型屬性進行管理一樣。
static?ssize_t?bus_uevent_store(struct?bus_type?*bus,?? ????????????????const?char?*buf,?size_t?count)?? {?? ????enum?kobject_action?action;?? ?? ????if?(kobject_action_type(buf,?count,?&action)?==?0)?? ????????kobject_uevent(&bus->p->subsys.kobj,?action);?? ????return?count;?? }?? static?BUS_ATTR(uevent,?S_IWUSR,?NULL,?bus_uevent_store);??
上面定義了bus的一個屬性uevent,用于bus所在的kset節點主動發起uevent消息。
同樣地uevent文件在driver目錄中也有見到。device目錄中也有,不過除了store_uevent之外,還增加了show_uevent的功能。
static?struct?device?*next_device(struct?klist_iter?*i)?? {?? ????struct?klist_node?*n?=?klist_next(i);?? ????struct?device?*dev?=?NULL;?? ????struct?device_private?*dev_prv;?? ?? ????if?(n)?{?? ????????dev_prv?=?to_device_private_bus(n);?? ????????dev?=?dev_prv->device;?? ????}?? ????return?dev;?? }?? ?? int?bus_for_each_dev(struct?bus_type?*bus,?struct?device?*start,?? ?????????????void?*data,?int?(*fn)(struct?device?*,?void?*))?? {?? ????struct?klist_iter?i;?? ????struct?device?*dev;?? ????int?error?=?0;?? ?? ????if?(!bus)?? ????????return?-EINVAL;?? ?? ????klist_iter_init_node(&bus->p->klist_devices,?&i,?? ?????????????????(start???&start->p->knode_bus?:?NULL));?? ????while?((dev?=?next_device(&i))?&&?!error)?? ????????error?=?fn(dev,?data);?? ????klist_iter_exit(&i);?? ????return?error;?? }?? ?? struct?device?*bus_find_device(struct?bus_type?*bus,?? ???????????????????struct?device?*start,?void?*data,?? ???????????????????int?(*match)(struct?device?*dev,?void?*data))?? {?? ????struct?klist_iter?i;?? ????struct?device?*dev;?? ?? ????if?(!bus)?? ????????return?NULL;?? ?? ????klist_iter_init_node(&bus->p->klist_devices,?&i,?? ?????????????????(start???&start->p->knode_bus?:?NULL));?? ????while?((dev?=?next_device(&i)))?? ????????if?(match(dev,?data)?&&?get_device(dev))?? ????????????break;?? ????klist_iter_exit(&i);?? ????return?dev;?? }??
bus_for_each_dev()是以bus的設備鏈表中每個設備為參數,調用指定的處理函數。
bus_find_device()是尋找bus設備鏈表中的某個設備,使用指定的匹配函數。
這兩個函數提供遍歷bus的設備鏈表的方法,類似于drivers_for_each_device/drivers_find_device對driver的設備鏈表的遍歷,device_for_each_child/device_find_child對device的子設備鏈表的遍歷。
static?int?match_name(struct?device?*dev,?void?*data)?? {?? ????const?char?*name?=?data;?? ?? ????return?sysfs_streq(name,?dev_name(dev));?? }?? ?? struct?device?*bus_find_device_by_name(struct?bus_type?*bus,?? ???????????????????????struct?device?*start,?const?char?*name)?? {?? ????return?bus_find_device(bus,?start,?(void?*)name,?match_name);?? }??
bus_find_device_by_name()給出了如何使用遍歷函數的例子,尋找bus設備鏈表中指定名稱的設備。
static?struct?device_driver?*next_driver(struct?klist_iter?*i)?? {?? ????struct?klist_node?*n?=?klist_next(i);?? ????struct?driver_private?*drv_priv;?? ?? ????if?(n)?{?? ????????drv_priv?=?container_of(n,?struct?driver_private,?knode_bus);?? ????????return?drv_priv->driver;?? ????}?? ????return?NULL;?? }?? ?? int?bus_for_each_drv(struct?bus_type?*bus,?struct?device_driver?*start,?? ?????????????void?*data,?int?(*fn)(struct?device_driver?*,?void?*))?? {?? ????struct?klist_iter?i;?? ????struct?device_driver?*drv;?? ????int?error?=?0;?? ?? ????if?(!bus)?? ????????return?-EINVAL;?? ?? ????klist_iter_init_node(&bus->p->klist_drivers,?&i,?? ?????????????????start???&start->p->knode_bus?:?NULL);?? ????while?((drv?=?next_driver(&i))?&&?!error)?? ????????error?=?fn(drv,?data);?? ????klist_iter_exit(&i);?? ????return?error;?? }??
bus_for_each_drv()對bus的驅動鏈表中的每個驅動調用指定的函數。
這和前面的bus_for_each_dev/bus_find_dev什么都是類似的,只是你可能懷疑為什么會沒有bus_find_drv。是沒有它的用武之地嗎?
請看driver.c中的driver_find()函數。
struct?device_driver?*driver_find(const?char?*name,?struct?bus_type?*bus)?? {?? ????struct?kobject?*k?=?kset_find_obj(bus->p->drivers_kset,?name);?? ????struct?driver_private?*priv;?? ?? ????if?(k)?{?? ????????priv?=?to_driver(k);?? ????????return?priv->driver;?? ????}?? ????return?NULL;?? }??
driver_find()函數是在bus的驅動鏈表中尋找指定名稱的驅動,它的存在證明bus_find_drv()完全是用得上的。可linux卻偏偏沒有實現bus_find_drv。driver_find()的實現也因此一直走內層路線,它直接用kset_find_obj()進行kobect的名稱匹配,調用to_driver()等內容將kobj轉化為drv。首先這完全不同于bus_for_each_drv()等一系列遍歷函數,它們走的都是在klist中尋找的路線,這里確實走的sysfs中kset內部鏈表。其次,這里其實也是獲得了drv的一個引用計數,在kset_find_obj()中會增加匹配的kobj的引用計數,driver_find()并沒有釋放,就相當于獲取了drv的一個引用計數。這樣雖然也可以,但代碼寫得很不優雅。可見人無完人,linux代碼還有許多可改進之處。當然,也可能在最新的linux版本中已經改正了。
static?int?bus_add_attrs(struct?bus_type?*bus)?? {?? ????int?error?=?0;?? ????int?i;?? ?? ????if?(bus->bus_attrs)?{?? ????????for?(i?=?0;?attr_name(bus->bus_attrs[i]);?i++)?{?? ????????????error?=?bus_create_file(bus,?&bus->bus_attrs[i]);?? ????????????if?(error)?? ????????????????goto?err;?? ????????}?? ????}?? done:?? ????return?error;?? err:?? ????while?(--i?>=?0)?? ????????bus_remove_file(bus,?&bus->bus_attrs[i]);?? ????goto?done;?? }?? ?? static?void?bus_remove_attrs(struct?bus_type?*bus)?? {?? ????int?i;?? ?? ????if?(bus->bus_attrs)?{?? ????????for?(i?=?0;?attr_name(bus->bus_attrs[i]);?i++)?? ????????????bus_remove_file(bus,?&bus->bus_attrs[i]);?? ????}?? }??
bus_add_attrs()將bus->bus_attrs中定義的屬性加入bus目錄。
bus_remove_attrs()將bus->bus_attrs中定義的屬性刪除。
開始看struct bus_type時我們說到結構中的bus_attrs、dev_attrs、drv_attrs三種屬性,后兩者分別在device_add_attrs()和driver_add_attrs()中添加,最后的bus_attrs也終于在bus_add_attrs()中得到添加。只是它們雖然都定義在bus_type中,確實添加在完全不同的三個地方。
static?void?klist_devices_get(struct?klist_node?*n)?? {?? ????struct?device_private?*dev_prv?=?to_device_private_bus(n);?? ????struct?device?*dev?=?dev_prv->device;?? ?? ????get_device(dev);?? }?? ?? static?void?klist_devices_put(struct?klist_node?*n)?? {?? ????struct?device_private?*dev_prv?=?to_device_private_bus(n);?? ????struct?device?*dev?=?dev_prv->device;?? ?? ????put_device(dev);?? }??
klist_devices_get()用于bus設備鏈表上添加節點時增加對相應設備的引用。
klist_devices_put()用于bus設備鏈表上刪除節點時減少對相應設備的引用。
相似的函數是device中的klist_children_get/klist_children_put,這是device的子設備鏈表。除此之外,bus的驅動鏈表和driver的設備鏈表,都沒有這種引用計數的保護。原因還未知,也許是linux覺得驅動不太靠譜,萬一突然當掉,也不至于影響device的正常管理。
? ? ? ? ? ? ? ?? int?bus_register(struct?bus_type?*bus)?? {?? ????int?retval;?? ????struct?bus_type_private?*priv;?? ?? ????priv?=?kzalloc(sizeof(struct?bus_type_private),?GFP_KERNEL);?? ????if?(!priv)?? ????????return?-ENOMEM;?? ?? ????priv->bus?=?bus;?? ????bus->p?=?priv;?? ?? ????BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);?? ?? ????retval?=?kobject_set_name(&priv->subsys.kobj,?"%s",?bus->name);?? ????if?(retval)?? ????????goto?out;?? ?? ????priv->subsys.kobj.kset?=?bus_kset;?? ????priv->subsys.kobj.ktype?=?&bus_ktype;?? ????priv->drivers_autoprobe?=?1;?? ?? ????retval?=?kset_register(&priv->subsys);?? ????if?(retval)?? ????????goto?out;?? ?? ????retval?=?bus_create_file(bus,?&bus_attr_uevent);?? ????if?(retval)?? ????????goto?bus_uevent_fail;?? ?? ????priv->devices_kset?=?kset_create_and_add("devices",?NULL,?? ?????????????????????????&priv->subsys.kobj);?? ????if?(!priv->devices_kset)?{?? ????????retval?=?-ENOMEM;?? ????????goto?bus_devices_fail;?? ????}?? ?? ????priv->drivers_kset?=?kset_create_and_add("drivers",?NULL,?? ?????????????????????????&priv->subsys.kobj);?? ????if?(!priv->drivers_kset)?{?? ????????retval?=?-ENOMEM;?? ????????goto?bus_drivers_fail;?? ????}?? ?? ????klist_init(&priv->klist_devices,?klist_devices_get,?klist_devices_put);?? ????klist_init(&priv->klist_drivers,?NULL,?NULL);?? ?? ????retval?=?add_probe_files(bus);?? ????if?(retval)?? ????????goto?bus_probe_files_fail;?? ?? ????retval?=?bus_add_attrs(bus);?? ????if?(retval)?? ????????goto?bus_attrs_fail;?? ?? ????pr_debug("bus:?'%s':?registered\n",?bus->name);?? ????return?0;?? ?? bus_attrs_fail:?? ????remove_probe_files(bus);?? bus_probe_files_fail:?? ????kset_unregister(bus->p->drivers_kset);?? bus_drivers_fail:?? ????kset_unregister(bus->p->devices_kset);?? bus_devices_fail:?? ????bus_remove_file(bus,?&bus_attr_uevent);?? bus_uevent_fail:?? ????kset_unregister(&bus->p->subsys);?? ????kfree(bus->p);?? out:?? ????bus->p?=?NULL;?? ????return?retval;?? }??
bus_register()將bus注冊到系統中。
先分配并初始化bus->p,名稱使用bus->name,所屬的kset使用bus_kset(果然不出所料),類型使用bus_ktype。bus_ktype的使用同driver中的driver_ktype,和device中的device_ktype一樣,都是自定義的kobj_type,要知道kobj_type的使用關系到release函數,和自定義屬性類型能否正常發揮。
調用kset_register()將bus加入sysfs,因為只是設置了kset,所以會被加入/sys/bus目錄下。與driver直接加入相關總線的drivers目錄類似,卻是與device復雜的尋找父節點過程相去甚遠。
在bus目錄下添加uevent屬性。
在bus目錄下創建devices子目錄。它是一個kset類型的,目的是展示bus下的設備鏈表。
在bus目錄下創建drivers子目錄。它也是一個kset類型的,目的是展示bus下的驅動鏈表。
或許在最開始有設備驅動模型時,還需要kset來表達這種鏈表關系,但隨著klist等結構的加入,kset的作用也越來越少,現在更多的作用是用來處理uevent消息。
之后初始化bus的設備鏈表和驅動鏈表,其中設備鏈表會占用設備的引用計數。
調用add_probe_files()在bus目錄下添加probe相關的兩個屬性文件。
調用bus_add_attrs添加bus結構中添加的屬性。?
bus_register()中的操作出乎意料的簡單。bus既不需要在哪里添加軟鏈接,也不需要主動向誰報道,從來都是device和driver到bus這里報道的。所以bus_register()中只需要初始一下結構,添加到sysfs中,添加相關的子目錄和屬性文件,就行了。
void?bus_unregister(struct?bus_type?*bus)?? {?? ????pr_debug("bus:?'%s':?unregistering\n",?bus->name);?? ????bus_remove_attrs(bus);?? ????remove_probe_files(bus);?? ????kset_unregister(bus->p->drivers_kset);?? ????kset_unregister(bus->p->devices_kset);?? ????bus_remove_file(bus,?&bus_attr_uevent);?? ????kset_unregister(&bus->p->subsys);?? ????kfree(bus->p);?? ????bus->p?=?NULL;?? }??
bus_unregister()與bus_register()相對,將bus從系統中注銷。不過要把bus注銷也不是那么簡單的,bus中的driver和device都對bus保有一份引用計數。或許正是如此,bus把釋放bus->p的動作放在了bus_unregister()中,這至少能保證較早地釋放不需要的內存空間。而且在bus引用計數用完時,也不會有任何操作,bus的容錯性還是很高的。
static?struct?bus_type?*bus_get(struct?bus_type?*bus)?? {?? ????if?(bus)?{?? ????????kset_get(&bus->p->subsys);?? ????????return?bus;?? ????}?? ????return?NULL;?? }?? ?? static?void?bus_put(struct?bus_type?*bus)?? {?? ????if?(bus)?? ????????kset_put(&bus->p->subsys);?? }??
bus_get()增加對bus的引用計數,bus_put()減少對bus的引用計數。實際上這里bus的引用計數降為零時,只是將sysfs中bus對應的目錄刪除。
無論是bus,還是device,還是driver,都是將主要的注銷工作放在相關的unregister中。至于在引用計數降為零時的操作,大概只在device_release()中可見。這主要是因為引用計數,雖然是廣泛用在設備驅動模型中,但實際支持的,絕大部分是設備的熱插拔,而不是總線或者驅動的熱插拔。當然,橋設備的熱插拔也可能附帶總線的熱插拔。
? ? ? ? ? ? ?? static?void?device_insertion_sort_klist(struct?device?*a,?struct?list_head?*list,?? ????????????????????int?(*compare)(const?struct?device?*a,?? ????????????????????????????const?struct?device?*b))?? {?? ????struct?list_head?*pos;?? ????struct?klist_node?*n;?? ????struct?device_private?*dev_prv;?? ????struct?device?*b;?? ?? ????list_for_each(pos,?list)?{?? ????????n?=?container_of(pos,?struct?klist_node,?n_node);?? ????????dev_prv?=?to_device_private_bus(n);?? ????????b?=?dev_prv->device;?? ????????if?(compare(a,?b)?<=?0)?{?? ????????????list_move_tail(&a->p->knode_bus.n_node,?? ???????????????????????&b->p->knode_bus.n_node);?? ????????????return;?? ????????}?? ????}?? ????list_move_tail(&a->p->knode_bus.n_node,?list);?? }?? ?? void?bus_sort_breadthfirst(struct?bus_type?*bus,?? ???????????????int?(*compare)(const?struct?device?*a,?? ??????????????????????const?struct?device?*b))?? {?? ????LIST_HEAD(sorted_devices);?? ????struct?list_head?*pos,?*tmp;?? ????struct?klist_node?*n;?? ????struct?device_private?*dev_prv;?? ????struct?device?*dev;?? ????struct?klist?*device_klist;?? ?? ????device_klist?=?bus_get_device_klist(bus);?? ?? ????spin_lock(&device_klist->k_lock);?? ????list_for_each_safe(pos,?tmp,?&device_klist->k_list)?{?? ????????n?=?container_of(pos,?struct?klist_node,?n_node);?? ????????dev_prv?=?to_device_private_bus(n);?? ????????dev?=?dev_prv->device;?? ????????device_insertion_sort_klist(dev,?&sorted_devices,?compare);?? ????}?? ????list_splice(&sorted_devices,?&device_klist->k_list);?? ????spin_unlock(&device_klist->k_lock);?? }??
bus_sort_breadthfirst()是將bus的設備鏈表進行排序,使用指定的比較函數,排成降序。
本節主要分析了bus的注冊注銷過程,下節我們將深入分析device和driver的綁定過程,了解bus在這其中到底起了什么作用。隨著我們了解的逐漸深入,未知的東西也在逐漸增多。但飯要一口一口吃,我們的分析也要一點一點來,急不得
總結
以上是生活随笔為你收集整理的linux内核组件分析之--设备驱动模型之bus的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。