linux驅動注冊過程分析--driver_register(一)
個人筆記,歡迎轉載,請注明出處,共同分享 共同進步?
http://blog.csdn.net/richard_liujh/article/details/45825333
kernel版本3.10.14
driver_register顧名思義,是驅動程序的注冊。但是很少是由我們寫的驅動直接調用的,例如framebuffer中調用platform_driver_register,i2c中調用i2c_add_driver等等函數注冊對應的驅動程序。雖然我們并沒有直接調用driver_register,但是最終都是通過driver_register幫我們完成了驅動程序的注冊。所以,了解driver_register的注冊過程,對我們理解linux的設備驅動有很到的幫助。
我們借助常用的platform_driver_register開始分析driver_register的調用過程。
1.初始化總線類型(bus_type),注冊probe等相關函數
在文件./drivers/base/platform.c 中有platform_driver_register源代碼:
[cpp] ?view plaincopy print?
? ? ? ?? int ?platform_driver_register( struct ?platform_driver?*drv)?? {?? ????drv->driver.bus?=?&platform_bus_type;?? ????if ?(drv->probe)?? ????????drv->driver.probe?=?platform_drv_probe;?? ????if ?(drv->remove)?? ????????drv->driver.remove?=?platform_drv_remove;?? ????if ?(drv->shutdown)?? ????????drv->driver.shutdown?=?platform_drv_shutdown;?? ?? ????return ?driver_register(&drv->driver);?? }?? EXPORT_SYMBOL_GPL(platform_driver_register);??
上面注冊了相應的probe remove shutdown 等函數后,開始調用driver_register
這里我們需要注意,driver的總線類型(bus_type)被初始化為platform_bus_type
[cpp] ?view plaincopy print?
drv->driver.bus?=?&platform_bus_type;??
其中platform_bus_type也在文件.
/drivers/base/platform.c
中有具體定義
[cpp] ?view plaincopy print?
struct ?bus_type?platform_bus_type?=?{?? ????.name???????=?"platform" ,?? ????.dev_attrs??=?platform_dev_attrs,?? ????.match??????=?platform_match,?? ????.uevent?????=?platform_uevent,?? ????.pm?????=?&platform_dev_pm_ops,?? };??
我們是已platform為例講解,所以注冊驅動的總線類型是platform的。如果是I2C總線呢?
其實也類似,例如在./drivers/i2c/i2c-core.c 中有I2C注冊函數i2c_register_driver源碼(省略部分無關代碼)
[cpp] ?view plaincopy print?
int ?i2c_register_driver( struct ?module?*owner,? struct ?i2c_driver?*driver)?? {?? ????……?? ????driver->driver.owner?=?owner;?? ????driver->driver.bus?=?&i2c_bus_type;?? ?????……?? }??
res = driver_register(&driver->driver);
……return 0;}EXPORT_SYMBOL(i2c_register_driver);
所以,如果注冊的是i2c驅動,那么總線類型初始化為i2c_bus_type,也可以在文件
./
drivers/i2c/i2c-core.c
中看到其定義
[cpp] ?view plaincopy print?
struct ?bus_type?i2c_bus_type?=?{?? ????.name???????=?"i2c" ,?? ????.match??????=?i2c_device_match,?? ????.probe??????=?i2c_device_probe,?? ????.remove?????=?i2c_device_remove,?? ????.shutdown???=?i2c_device_shutdown,?? ????.pm?????=?&i2c_device_pm_ops,?? };??
當總線類型和probe、remove、shutdown等函數注冊后,就開始調用driver_register注冊對應的驅動了。
driver_register源代碼在文件./drivers/base/driver.c 中
[cpp] ?view plaincopy print?
? ? ? ? ? ? ? ?? int ?driver_register( struct ?device_driver?*drv)?? {?? ????int ?ret;?? ????struct ?device_driver?*other;?? ?? ????BUG_ON(!drv->bus->p);?? ?? ????if ?((drv->bus->probe?&&?drv->probe)?||?? ????????(drv->bus->remove?&&?drv->remove)?||?? ????????(drv->bus->shutdown?&&?drv->shutdown))?? ????????printk(KERN_WARNING?"Driver?'%s'?needs?updating?-?please?use?" ?? ????????????"bus_type?methods\n" ,?drv->name);?? ?? ????other?=?driver_find(drv->name,?drv->bus);?? ????if ?(other)?{?? ????????printk(KERN_ERR?"Error:?Driver?'%s'?is?already?registered,?" ?? ????????????"aborting...\n" ,?drv->name);?? ????????return ?-EBUSY;?? ????}?? ?? ????ret?=?bus_add_driver(drv);?? ????if ?(ret)?? ????????return ?ret;?? ????ret?=?driver_add_groups(drv,?drv->groups);?? ????if ?(ret)?{?? ????????bus_remove_driver(drv);?? ????????return ?ret;?? ????}?? ????kobject_uevent(&drv->p->kobj,?KOBJ_ADD);?? ?? ????return ?ret;?? }?? EXPORT_SYMBOL_GPL(driver_register);??
為了更好閱讀上面的代碼,我將其化簡如下
[cpp] ?view plaincopy print?
int ?driver_register( struct ?device_driver?*drv)?? ????|?? ????|-->?driver_find??? ????|-->?bus_add_driver?? ????|-->?driver_add_groups?? ????|-->?kobject_uevent??
2. driver_find分析
在driver_register中調用driver_find,driver_find名字很通俗易懂,可以簡單理解為找“驅動”。由于從linux 2.6版本,內核采用設備驅動模型,所以所謂的“找驅動“還是了解一點設備驅動模型的知識比較好。
在文件./drivers/base/driver.c 中有driver_find源碼
[cpp] ?view plaincopy print?
? ? ? ? ? ? ? ? ? ? ? ?? 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)?{?? ?????????? ????????kobject_put(k);?? ????????priv?=?to_driver(k);?? ????????return ?priv->driver;?? ????}?? ????return ?NULL;?? }?? EXPORT_SYMBOL_GPL(driver_find);??
我們注意通過注釋和代碼知道,driver_find 通過我們給定的name 在某bus 中尋找驅動 。這個比較好理解,就像上學的時候,老師XX知道某個學生的名字(name),然后去他所在的班級(bus)找這個學生。如果找到過(一般沒好事TT),就把學生叫出來好好教育一番....。那么driver_find找了所謂的驅動會怎樣呢?我們觀察driver_find的返回值,你會發現,這里返回的是指針,也就是說driver_find是一個指針函數嘍。指針的類型是struct?device_driver 類型的。
struct device_driver ?在文件?include/linux/device.h 中定義
[cpp] ?view plaincopy print?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? struct ?device_driver?{?? ????const ? char ??????*name;?? ????struct ?bus_type?????*bus;?? ?? ????struct ?module???????*owner;?? ????const ? char ??????*mod_name;?? ?? ?? ????bool ?suppress_bind_attrs;??? ?? ?? ????const ? struct ?of_device_id???*of_match_table;?? ????const ? struct ?acpi_device_id?*acpi_match_table;?? ?? ????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 ?attribute_group?**groups;?? ?? ????const ? struct ?dev_pm_ops?*pm;?? ?? ????struct ?driver_private?*p;?? };??
這個結構體里面包含了設備驅動的重要信息,例如名字(name)、總線類型(bus)、所述模塊(owner)和一些用于回調的函數指針(probe,remove,suspend...)。總而言之,得到此指針就像得到了驅動,就像得民心者得天下
....
/*******************************************************************************************************************************
下面涉及到設備驅動,這里只是簡單提一下,一時看不懂很正常。如果有時間還想把設備驅動專門寫幾篇博文
*******************************************************************************************************************************/
那么問題來了,driver_find到底是如何通過name在bus中尋找驅動呢。其實就是通過下面的代碼實現的
[cpp] ?view plaincopy print?
struct ?kobject?*k?=?kset_find_obj(bus->p->drivers_kset,?name);??
其中kset_find_obj貌似很高端的樣子,這又得談到linux的設備模型了。linux2.6為了更好的管理,加入了一系列”面向對象“概念,說簡單點就是更好的管理資源。例如一些資源占用了內存空間,但是卻沒有人去使用,這種資源其實是可以從內存中被釋放的。
所以實現了基本的面向對象管理機制,是構成Linux2.6設備模型的核心結構。它與sysfs文件系統緊密相連,在內核中注冊的每個kobject對象對應sysfs文件系統中的一個目錄。類似于C++中的基類,Kobject常被嵌入于其他類型(即:容器)中。如bus,devices,drivers都是典型的容器。這些容器通過kobject連接起來,形成了一個樹狀結構。Bus:在內核中注冊的每條總線在該目錄下對應一個子目錄,如: i2c platform spi ide pci scsi等等?其中每個總線目錄內又包含兩個子目錄:devices和drivers ,devices目錄包含了在整個系統中發現的屬于該總線類型的設備,drivers目錄包含了注冊到該總線。其實說這么多,就是想讓讀者了解一點,我們的driver和bus類型、Kobject,kset等有莫大的關聯 。至于具體的原理,大家可以自己找一些設備驅動的資料看看。這里就不詳細說明了。
?在文件./lib/kobject.c? ?文件中有kset_find_obj函數的源碼
[cpp] ?view plaincopy print?
?*?kset_find_obj?-?search? for ?object?in?kset.?? ?*?@kset:?kset?we're?looking?in.?? ?*?@name:?object's?name.?? ?*?? ?*?Lock?kset?via?@kset->subsys,?and?iterate?over?@kset->list,?? ?*?looking?for ?a?matching?kobject.?If?matching?object?is?found?? ?*?take?a?reference?and?return ?the?object.?? ?*/?? struct ?kobject?*kset_find_obj( struct ?kset?*kset,? const ? char ?*name)?? {?? ????struct ?kobject?*k;?? ????struct ?kobject?*ret?=?NULL;?? ?? ????spin_lock(&kset->list_lock);?? ?? ????list_for_each_entry(k,?&kset->list,?entry)?{?? ????????if ?(kobject_name(k)?&&?!strcmp(kobject_name(k),?name))?{?? ????????????ret?=?kobject_get_unless_zero(k);?? ????????????break ;?? ????????}?? ????}?? ?? ????spin_unlock(&kset->list_lock);?? ????return ?ret;?? }??
這里面涉及到了一個很常用很的宏函數list_for_each_entry,不知道的童鞋可以
點擊這里
。kset_find_obj通過循環操作,,根據我們給的名字name在指定的bus中循環對比,查看是否有相同的名字name(這個name存放在kobj中)。其實這就是一個循環鏈表的遍歷過程,kset和kobj里面都有鏈表指針next和prev。kset是a set of kobjects,kobj是kernel object,所以kset是一系列的kobj的組合。其中kset,內核中的解釋是struct kset - a set of kobjects of a specific type, belonging to a specific subsystem.那么這里有個重要的belonging to啦,也就是現在分詞做定語
。哈哈,belonging to a specific subsystem說的是kset(一系列kobjs)屬于特定的子系統。所以,初學者我們可以這么思考,一個kobj應該是屬于某個kset(或者說kobj在kset循環鏈表中),kset又是屬于某個subsystem的。所以,我們要通過name去尋找驅動,就必須要知道bustype,然后得到kset,最后得到kobj才能去對比name是否相同。這時我們回頭看看調用driver_find(drv->name, drv->bus);時,不就給了
drv->bus
,然后通過
bus->p->drivers_kset
得到了kset。
總結driver_find 過程如下:
1. driver_find,拿到了drv->name和drv->bus開始找驅動
2. kset_find_obj 通過driver_find傳遞的bus->p->drivers_kset,利用list_for_each_entry遍歷kset循環鏈表。(kset結構體中有循環鏈表指針next和prev)
3. 遍歷循環鏈表中每一個kobj中的成員變量name
4. 通過strcmp(kobject_name(k), name) 比較drv->name 和kobj中的name,如果有相同則表示查找成功
5. return :如果找到,則返回device_driver的指針,如果沒有找到則返回了NULL。
為了能更好的說明driver_find,我用下面的圖示意一下。
通過下面driver_register的代碼可以看出調用driver_find的作用,
[cpp] ?view plaincopy print?
other?=?driver_find(drv->name,?drv->bus);?? if ?(other)?{?? ????printk(KERN_ERR?"Error:?Driver?'%s'?is?already?registered,?" ?? ????????"aborting...\n" ,?drv->name);?? ????return ?-EBUSY;?? }??
通過判斷driver_find的返回值other,如果
if(other)
條件成立,說明other
不是NULL
,也就是說driver_find查找成功。但driver_register是注冊驅動程序,如果驅動已經注冊過,就不需要再次注冊了。如果已經注冊,那么直接
return -EBUSY;
后面的操作就不需要進行了。
所以driver_register調用driver_find是為了檢驗驅動是否已經被注冊,防止重復注冊。
總結
以上是生活随笔 為你收集整理的linux驱动篇之 driver_register 过程分析(一) 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。