linux驅動註冊過程分析--driver_register(一)
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 plain copy print?- /**
- * 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
- }
- EXPORT_SYMBOL_GPL(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
[cpp] view plain copy print?- drv->driver.bus = &platform_bus_type;
drv->driver.bus = &platform_bus_type;
其中platform_bus_type也在檔案./drivers/base/platform.c中有具體定義[cpp] view plain copy 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,
- };
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 plain copy print?- int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
- {
- ……
- driver->driver.owner = owner;
- driver->driver.bus = &i2c_bus_type;
- ……
- }
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 plain
copy
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,
- };
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 plain copy print?- /**
- * 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);
/**
* 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);
為了更好閱讀上面的程式碼,我將其化簡如下
[cpp]
view plain
copy
print?
- int driver_register(struct device_driver *drv)
- |
- |--> driver_find //查詢驅動是否已經裝載
- |--> bus_add_driver//根據匯流排型別新增驅動
- |--> driver_add_groups//將驅動新增到對應組中
- |--> kobject_uevent//註冊uevent事件
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原始碼
[cpp] view plain copy print?- /**
- * 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(constchar *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 - 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 {
- constchar *name;
- struct bus_type *bus;
- struct module *owner;
- constchar *mod_name; /* used for built-in modules */
- bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
- conststruct of_device_id *of_match_table;
- conststruct 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);
- conststruct attribute_group **groups;
- conststruct dev_pm_ops *pm;
- struct driver_private *p;
- };
/**
* 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);
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 plain copy 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, constchar *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;
- }
* 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 plain copy 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;
- }
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是為了檢驗驅動是否已經被註冊,防止重複註冊。