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 版權宣告:本文為博主原創文章,轉載請附上博文連結!