1. 程式人生 > >linux驅動篇之 driver_register 過程分析(一)

linux驅動篇之 driver_register 過程分析(一)

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原始碼:

/**  * platform_driver_register - register a driver for platform-level devices  * @drv: platform driver structure  */ 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

drv->driver.bus = &platform_bus_type; 其中platform_bus_type也在檔案./drivers/base/platform.c中有具體定義 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原始碼(省略部分無關程式碼)

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中看到其定義 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中

/**  * driver_register - register driver with bus  * @drv: driver to register  *  * We pass off most of the work to the bus_add_driver() call,  * since most of the things we have to do deal with the bus  * structures.  */ 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); 為了更好閱讀上面的程式碼,我將其化簡如下 int driver_register(struct device_driver *drv)     |     |--> driver_find //查詢驅動是否已經裝載     |--> bus_add_driver//根據匯流排型別新增驅動     |--> driver_add_groups//將驅動新增到對應組中     |--> kobject_uevent//註冊uevent事件

2. driver_find分析

在driver_register中呼叫driver_find,driver_find名字很通俗易懂,可以簡單理解為找“驅動”。由於從linux 2.6版本,核心採用裝置驅動模型,所以所謂的“找驅動“還是瞭解一點裝置驅動模型的知識比較好。

在檔案./drivers/base/driver.c中有driver_find原始碼

/**  * driver_find - locate driver on a bus by its name.  * @name: name of the driver.  * @bus: bus to scan for the driver.  *  * Call kset_find_obj() to iterate over list of drivers on  * a bus to find driver by name. Return driver if found.  *  * This routine provides no locking to prevent the driver it returns  * from being unregistered or unloaded while the caller is using it.  * The caller is responsible for preventing this.  */ 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) {         /* Drop reference added by kset_find_obj() */         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中定義

/**  * struct device_driver - The basic device driver structure  * @name:    Name of the device driver.  * @bus:    The bus which the device of this driver belongs to.  * @owner:    The module owner.  * @mod_name:    Used for built-in modules.  * @suppress_bind_attrs: Disables bind/unbind via sysfs.  * @of_match_table: The open firmware table.  * @acpi_match_table: The ACPI match table.  * @probe:    Called to query the existence of a specific device,  *        whether this driver can work with it, and bind the driver  *        to a specific device.  * @remove:    Called when the device is removed from the system to  *        unbind a device from this driver.  * @shutdown:    Called at shut-down time to quiesce the device.  * @suspend:    Called to put the device to sleep mode. Usually to a  *        low power state.  * @resume:    Called to bring a device from sleep mode.  * @groups:    Default attributes that get created by the driver core  *        automatically.  * @pm:        Power management operations of the device which matched  *        this driver.  * @p:        Driver core's private data, no one other than the driver  *        core can touch this.  *  * The device driver-model tracks all of the drivers known to the system.  * The main reason for this tracking is to enable the driver core to match  * up drivers with new devices. Once drivers are known objects within the  * system, however, a number of other things become possible. Device drivers  * can export information and configuration variables that are independent  * of any specific device.  */ struct device_driver {     const char        *name;     struct bus_type        *bus;       struct module        *owner;     const char        *mod_name;    /* used for built-in modules */       bool suppress_bind_attrs;    /* disables bind/unbind via sysfs */       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中尋找驅動呢。其實就是通過下面的程式碼實現的

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函式的原始碼

 * 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的作用,

    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是為了檢驗驅動是否已經被註冊,防止重複註冊。 ---------------------  作者:Richard_LiuJH  來源:CSDN  原文:https://blog.csdn.net/richard_liujh/article/details/45825333  版權宣告:本文為博主原創文章,轉載請附上博文連結!