日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux内核组件分析之--设备驱动模型之bus

發布時間:2024/9/21 linux 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux内核组件分析之--设备驱动模型之bus 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
前面我們分析了設備驅動模型中的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,??
  • };??
  • ??
  • /*?kset?to?create?/sys/devices/??*/??
  • 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的正常管理。

  • /**?
  • ?*?bus_register?-?register?a?bus?with?the?system.?
  • ?*?@bus:?bus.?
  • ?*?
  • ?*?Once?we?have?that,?we?registered?the?bus?with?the?kobject?
  • ?*?infrastructure,?then?register?the?children?subsystems?it?has:?
  • ?*?the?devices?and?drivers?that?belong?to?the?bus.?
  • ?*/??
  • 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()中可見。這主要是因為引用計數,雖然是廣泛用在設備驅動模型中,但實際支持的,絕大部分是設備的熱插拔,而不是總線或者驅動的熱插拔。當然,橋設備的熱插拔也可能附帶總線的熱插拔。

  • /*?
  • ?*?Yes,?this?forcably?breaks?the?klist?abstraction?temporarily.??It?
  • ?*?just?wants?to?sort?the?klist,?not?change?reference?counts?and?
  • ?*?take/drop?locks?rapidly?in?the?process.??It?does?all?this?while?
  • ?*?holding?the?lock?for?the?list,?so?objects?can't?otherwise?be?
  • ?*?added/removed?while?we're?swizzling.?
  • ?*/??
  • 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的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。