本文將簡要地介紹Linux總線設備驅動模型及其實現方式,最後,本文將會以平臺總線爲例介紹設備和驅動程序的實現過程。
1 總線設備驅動模型總體介紹及其實現方式
1.1 總線設備驅動模型總體介紹
如果cpu和外設或者外設和外設想要進行通訊,需要將通信的雙方掛載到一條總線上,這裏的總線可以是具體的總線,如IIC、CAN總線等,也可以是虛擬的總線,如平臺總線。隨着技術的不斷進步,系統的拓撲結構也越來越複雜,對熱插拔,跨平臺移植性的要求也越來越高,2.4內核已經難以滿足這些需求。爲適應這種形勢的需要,從Linux 2.6內核開始提供了全新的設備模型(當然也可以選擇不用這樣的方式實現)。其示意圖如下所示:
總線設備驅動模型可分爲總線、設備和驅動三個部分,當將一個設備加入到總線上時,內核會在這條總線上尋找該設備對應的驅動;當將一個驅動加入到一條總線上時,內核會在該總線上尋找與該驅動對應的設備。匹配的規則根據不同類型的總線及設備特徵進行定義。
1.2 總線實現方式
(1):總線的描述
在 Linux 內核中, 總線由 bus_type 結構表示,定義在 <linux/device.h>中
struct bus_type {
const char *name; /*總線名稱*/
int (*match) (struct device *dev, struct
device_driver *drv); /*驅動與設備的匹配函數*/
………
}
其中,match函數的定義爲:
int (*match)(struct device * dev, struct device_driver * drv)
設備和驅動的匹配規則在此函數中定義,match函數的職責爲:當一個新設備或者新驅動被添加到這個總線時,該函數被調用,用於判斷指定的驅動程序是否能處理指定的設備。若可以,則返回非零,若不可以則返回0值。
(2):總線的註冊和註銷
總線的註冊使用如下函數
bus_register(struct bus_type *bus)
若成功,新的總線將被添加進系統,並可在/sys/bus 下看到相應的目錄。
總線的註銷使用:
void bus_unregister(struct bus_type *bus)
若成功,則總線將會從系統中刪除。
1.3 設備的實現方式
(1):設備的描述方式
在 Linux內核中, 設備由struct device結構表示。
struct device {
{
const char *init_name; /*設備的名字*/
struct bus_type *bus; /*設備所在的總線*/
………
}
其中,init_name爲設備的名稱,*bus指向總線的描述結構。
(2):設備的註冊和註銷
設備的註冊使用如下函數:
int device_register(struct device *dev);
當一個設備成功地添加到一條總線上時可以在/sys/bus/xxx_bus/目錄下看到相應的設備。
設備的註銷使用如下函數:
void device_unregister(struct device *dev);
1.4 驅動實現方式
(1):驅動的描述方式
在 Linux內核中, 驅動由 device_driver結構表示:
struct device_driver {
{
const char *name; /*驅動名稱*/
struct bus_type *bus; /*驅動程序所在的總線*/
int (*probe) (struct device *dev);/*設備匹配成功後調用的函數*/
………
}
其中,*name爲驅動的名稱,可以作爲一種設備和驅動匹配的條件;*bus指向所要加入的總線的描述結構;*probe函數指針指向一個操作函數,該函數會在設備和驅動匹配成功之後(即match函數返回爲1時)被調用,一般用來完成對設備的初始化操作。
(2):驅動的註冊和註銷
驅動的註冊使用如下函數
int driver_register(struct device_driver *drv);
驅動的註銷使用:
void driver_unregister(struct device_driver *drv);
2 平臺設備驅動舉例
上面介紹了總線驅動的相關數據結構,下面以平臺設備驅動爲例進一步分析。有了上面的總線驅動的基本數據結構,linux其他總線驅動都是對上面數據結構的再封裝。
下面先列舉一個簡單按鍵驅動:
2.1 device 註冊
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
MODULE_LICENSE("GPL");
#define GPFCON 0x56000050
//所用的資源描述
static struct resource key_resource[] = {
[0] = {
.start = GPFCON,
.end = GPFCON + 8,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_EINT0,
.end = IRQ_EINT2,
.flags = IORESOURCE_IRQ,
},
};
struct platform_device key_device = {
.name = "my-key",
.id = 0,
.num_resources = ARRAY_SIZE(key_resource),
.resource = key_resource,
};
static int button_init()
{
platform_device_register(&key_device);
return 0;
}
static void button_exit()
{
platform_device_unregister(&key_device);
}
module_init(button_init);
module_exit(button_exit);
2.2 driver註冊
#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/platform_device.h>
MODULE_LICENSE("GPL");
struct work_struct *work;
struct timer_list buttons_timer;
unsigned int key_num = 0;
wait_queue_head_t key_q;
struct resource *res;
struct resource *res_irq;
unsigned int *key_base;
void work_func(struct work_struct *work)
{
mod_timer(&buttons_timer, jiffies + (HZ /10));
}
void buttons_timer_function(unsigned long data)
{
unsigned int key_val;
key_val = readw(key_base+1)&0x1;
if (key_val == 0)
key_num = 4;
key_val = readw(key_base+1)&0x4;
if (key_val == 0)
key_num = 3;
wake_up(&key_q);
}
irqreturn_t key_int(int irq, void *dev_id)
{
//1. 檢測是否發生了按鍵中斷
//2. 清除已經發生的按鍵中斷
//3. 提交下半部
schedule_work(work);
//return 0;
return IRQ_HANDLED;
}
void key_hw_init()
{
unsigned short data;
data = readw(key_base);
data &= ~0b110011;
data |= 0b100010;
writew(data,key_base);
}
int key_open(struct inode *node,struct file *filp)
{
return 0;
}
ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos)
{
wait_event(key_q,key_num);
copy_to_user(buf, &key_num, 4);
key_num = 0;
return 4;
}
struct file_operations key_fops =
{
.open = key_open,
.read = key_read,
};
struct miscdevice key_miscdev = {
.minor = 200,
.name = "key",
.fops = &key_fops,
};
int key_probe(struct platform_device *pdev)
{
int ret,size;
ret = misc_register(&key_miscdev);
if (ret !=0)
printk("register fail!\n");
//註冊中斷處理程序
res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
request_irq(res_irq->start,key_int,IRQF_TRIGGER_FALLING,"key",(void *)4);
request_irq(res_irq->end,key_int,IRQF_TRIGGER_FALLING,"key",(void *)3);
//按鍵初始化
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
size = (res->end - res->start) + 1;
key_base = ioremap(res->start, size);
key_hw_init();
//. 創建工作
work = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
INIT_WORK(work, work_func);
/* 初始化定時器 */
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;
/* 向內核註冊一個定時器 */
add_timer(&buttons_timer);
/*初始化等待隊列*/
init_waitqueue_head(&key_q);
return 0;
}
int key_remove(struct platform_device *dev)
{
free_irq(res_irq->start, (void *)4);
free_irq(res_irq->end, (void *)3);
iounmap(key_base);
misc_deregister(&key_miscdev);
return 0;
}
static struct platform_driver key_driver = {
.probe = key_probe,
.remove = key_remove,
.driver = {
.owner = THIS_MODULE,
.name = "my-key",
},
};
static int button_init()
{
return platform_driver_register(&key_driver);
}
static void button_exit()
{
platform_driver_unregister(&key_driver);
}
module_init(button_init);
module_exit(button_exit);
3 平臺設備驅動詳解
3.1 平臺總線初始化
系統初始化的時候,會初始化平臺總線:
kernel_init
----------->kernel_init_freeable
-------------->do_basic_setup
---------------->driver_init
------------------>platform_bus_init
struct device platform_bus = {
.init_name = "platform",
};
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
int __init platform_bus_init(void)
{
int error;
early_platform_cleanup();
-------------------------------------------------------------(1)
error = device_register(&platform_bus);//平臺總線也被抽象成一個設備,所以註冊平臺總線設備
if (error)
return error;
-------------------------------------------------------------(2)
error = bus_register(&platform_bus_type);//註冊平臺總線
if (error)
device_unregister(&platform_bus);
return error;
}
(1)平臺總線也被抽象成一個設備,device_register的詳細說明,可以參考這篇文章:
https://blog.csdn.net/oqqYuJi12345678/article/details/103057226
該函數會在/sys/devices/下面註冊platform文件夾,後面註冊的平臺設備會放到該platform文件夾下
(2)向內核總線系統註冊平臺總線
內核通過 bus_register 進行 Bus 註冊,註冊到了 bus_kest
系統初始化的時候有如下代碼:
int __init buses_init(void)
{
// /sys/bus 目錄 這裏創建的
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
if (!bus_kset)
return -ENOMEM;
return 0;
}
下面看一下bus_register具體做了什麼:
int bus_register(struct bus_type *bus)
{
int retval;
struct bus_type_private *priv;
priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
/* 1. bus 與 prv 相互建立聯繫 */
// 私有數據 .bus -> bus 本身
priv->bus = bus;
// bus->p 指向 priv
bus->p = priv;
// 內核通知鏈
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
/* 設置 bus->prv->subsys->kobj */
// 設置 priv->subsys.kobj.name = bus->name 對應於/sys/ 目錄下的目錄名
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
// 所有的 priv->subsys.kobj.kset 指向 bus_kse 對應於圖中④與六的關係
priv->subsys.kobj.kset = bus_kset;
// 所有的priv->subsys.kobj.ktype 等於 bus_ktype
priv->subsys.kobj.ktype = &bus_ktype;
priv->drivers_autoprobe = 1;//device和driver註冊的時候會檢查該位
/* 註冊 kset (bus->prv->subsys priv->devices_kset priv->drivers_kset) */
// 註冊 priv->subsys ,由於 priv->subsys.kobj.kset = bus_kset,所以會在 /sys/bus/目錄下創建 目錄 如/sys/bus/platform
retval = kset_register(&priv->subsys);
// sysfs_create_file(&bus->p->subsys.kobj, &bus_attr_uevent->attr);
retval = bus_create_file(bus, &bus_attr_uevent);
// 由於 priv->subsys.kobj.kset = bus_kset ,因此會創建 /sys/bus/XXX/devices 目錄 如 /sys/bus/platform/devices
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
// 同理 創建 /sys/bus/XXX/devices 目錄 如 /sys/bus/platform/drivers
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);
// 初始化 klist_devices 並設置get put 函數 初始化 klist_drivers 不知爲何沒有get put ?
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
retval = add_probe_files(bus); // static inline int add_probe_files(struct bus_type *bus) { return 0; }
// 添加 bus->attrs 屬性文件
retval = bus_add_attrs(bus);
return 0;
}
上面函數主要是在/sys/bus/下面註冊了platform目錄,然後在/sys/bus/platform/目錄下面創建devices和drivers目錄,以及其他的一些屬性文件。
看一下/sys/bus/platform/具體有哪些文件和目錄:
3.2 平臺設備註冊
上面的平臺設備驅動調用了platform_device_register函數註冊device:
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
arch_setup_pdev_archdata(pdev);
return platform_device_add(pdev);
}
platform_device_register函數先通過device_initialize函數初始化platform_device的device成員,然後調用platform_device_add向內核添加一個平臺設備。
int platform_device_add(struct platform_device *pdev)
{
int i, ret = 0;
if (!pdev) /* 如果pdev爲空則返回EINVAL */
return -EINVAL;
/* 如果pdev->dev.parent爲空則將pdev->dev.parent設置爲platform_bus */
if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus;
pdev->dev.bus = &platform_bus_type; /* 設置總線類型 */
if (pdev->id != -1) /* 如果id = -1則表示自動分配name */
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
else
dev_set_name(&pdev->dev, pdev->name);
for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource[i]; /* 獲取資源 */
if (r->name == NULL)
r->name = dev_name(&pdev->dev);
p = r->parent;
if (!p) {
if (resource_type(r) == IORESOURCE_MEM) /* 設置資源類型 */
p = &iomem_resource;
else if (resource_type(r) == IORESOURCE_IO)
p = &ioport_resource;
}
if (p && insert_resource(p, r)) {
printk(KERN_ERR
"%s: failed to claim resource %d\n",
dev_name(&pdev->dev), i);
ret = -EBUSY;
goto failed;
}
}
pr_debug("Registering platform device '%s'. Parent at %s\n",
dev_name(&pdev->dev), dev_name(pdev->dev.parent));
/* 向內核添加一個device */
ret = device_add(&pdev->dev);
if (ret == 0)
return ret;
failed:
while (--i >= 0) {
struct resource *r = &pdev->resource[i];
unsigned long type = resource_type(r);
if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
release_resource(r);
}
return ret;
}
可以看到platform_device_register函數就是對device_add的封裝。已經討論過幾回了,現在重點關注device_add中的幾個部分:
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct kobject *kobj;
struct class_interface *class_intf;
int error = -EINVAL;
..........................................................
//在上面函數中dev->parent被設置爲platform_bus,所以後面會在/sys/devices/platform/下面創建設備文件
parent = get_device(dev->parent);
kobj = get_device_parent(dev, parent);
if (kobj)
dev->kobj.parent = kobj;
/* use parent numa_node */
if (parent)
set_dev_node(dev, dev_to_node(parent));
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL */
//在/sys/devices/platform/下面創建設備文件
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
if (error)
goto Error;
..............................................................
--------------------------------------------------------(1)
error = bus_add_device(dev);
.................................................
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
kobject_uevent(&dev->kobj, KOBJ_ADD);
-----------------------------------------------------------(2)
bus_probe_device(dev);
if (parent)//添加到bus的鏈表上
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
}
(1)bus_add_device的最重要工作是把device添加到bus的鏈表中:
int bus_add_device(struct device *dev)
{
struct bus_type *bus = bus_get(dev->bus);
int error = 0;
if (bus) {
pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
error = device_add_attrs(bus, dev);
if (error)
goto out_put;
error = sysfs_create_link(&bus->p->devices_kset->kobj,
&dev->kobj, dev_name(dev));
if (error)
goto out_id;
error = sysfs_create_link(&dev->kobj,
&dev->bus->p->subsys.kobj, "subsystem");
if (error)
goto out_subsys;
klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);//把device添加到bus鏈表中
}
return 0;
out_subsys:
sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
out_id:
device_remove_attrs(bus, dev);
out_put:
bus_put(dev->bus);
return error;
}
(2)bus_probe_device遍歷bus上面的driver鏈表,看看是否有匹配的drvier要probe:
void bus_probe_device(struct device *dev)
{
struct bus_type *bus = dev->bus;
struct subsys_interface *sif;
int ret;
if (!bus)
return;
if (bus->p->drivers_autoprobe) { //bus在之前註冊時候設置了drivers_autoprobe
ret = device_attach(dev);//執行該函數
WARN_ON(ret < 0);
}
mutex_lock(&bus->p->mutex);
list_for_each_entry(sif, &bus->p->interfaces, node)
if (sif->add_dev)
sif->add_dev(dev, sif);
mutex_unlock(&bus->p->mutex);
}
調用device_attach進行匹配:
int device_attach(struct device *dev)
{
int ret = 0;
device_lock(dev);
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
pm_request_idle(dev);
out_unlock:
device_unlock(dev);
return ret;
}
主要函數爲bus_for_each_drv:
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)//遍歷bus->p->klist_drivers鏈表,調用__device_attach進行匹配
error = fn(drv, data);
klist_iter_exit(&i);
return error;
}
依次調用bus上面掛載的driver,利用__device_attach函數進行匹配:
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
static int __device_attach(struct device_driver *drv, void *data)
{
struct device *dev = data;
if (!driver_match_device(drv, dev))
return 0;
return driver_probe_device(drv, dev);
}
匹配成功以後,driver_match_device函數返回不爲0,進一步調用driver_probe_device函數,進行driver probe。
從前面知道platform總線的match函數爲platform_match:
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv))
return 1;
/* Then try ACPI style match */
if (acpi_driver_match_device(dev, drv))
return 1;
/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}
可以看到較新版的內核除了匹配設備和驅動名字,還有其他多種匹配手段了。再看一下driver_probe_device函數:
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
//device已經註冊過,則不再probe
if (!device_is_registered(dev))
return -ENODEV;
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
pm_runtime_barrier(dev);
ret = really_probe(dev, drv);
pm_request_idle(dev);
return ret;
}
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
dev->driver = drv; //device 指向driver
。。。。。。。。。。。。。。。。。。。
ret = drv->probe(dev);//調用driver的probe函數
。。。。。。。。。。。。。。。。。。。。
driver_bound(dev);//driver和device綁定
ret = 1;
}
static void driver_bound(struct device *dev)
{
。。。。。。。。。。。。。。。。。。。。
//把device 掛入driver鏈表,一個driver可以匹配多個device
klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
/*
* Make sure the device is no longer in one of the deferred lists and
* kick off retrying all pending devices
*/
driver_deferred_probe_del(dev);
driver_deferred_probe_trigger();
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_BOUND_DRIVER, dev);
}
可以看到最後會調用driver的probe函數,並且把device掛入driver的鏈表中。一個driver可以對應多個device。需要注意的是,上面really_probe裏面調用的probe函數還不是真正平臺驅動的probe函數,他是平臺設備驅動框架提供的probe函數platform_drv_probe:
static int platform_drv_probe(struct device *_dev)
{
struct platform_driver *drv = to_platform_driver(_dev->driver);//獲取平臺驅動結構
struct platform_device *dev = to_platform_device(_dev);
int ret;
if (ACPI_HANDLE(_dev))
acpi_dev_pm_attach(_dev, true);
ret = drv->probe(dev);//調用平臺驅動的probe
if (ret && ACPI_HANDLE(_dev))
acpi_dev_pm_detach(_dev, true);
return ret;
}
3.4 平臺驅動註冊
上面的例子中調用platform_driver_register來向內核註冊平臺驅動:
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;
if (drv->probe)//platform_driver 設置了probe函數,則device_driver需要設置platform_drv_probe,匹配的時候先會進入通用驅動框架,調用device_driver的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);
}
driver_register的核心函數爲bus_add_driver
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
bus = bus_get(drv->bus);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
// 在/sys/bus/platform/drivers 目錄下創建目錄
priv->kobj.kset = bus->p->drivers_kset;
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
// 匹配 dev
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
}
// 將driver 加入 Bus->p->kist_drivers鏈表
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
// 如果設置了drv->mod_name 根據名字尋找模塊
module_add_driver(drv->owner, drv);
// 在/sys/bus/platform/drivers/創建屬性文件
error = driver_create_file(drv, &driver_attr_uevent);
error = driver_add_attrs(bus, drv);
if (!drv->suppress_bind_attrs) {
error = add_bind_files(drv);
}
kobject_uevent(&priv->kobj, KOBJ_ADD);
return 0;
}
在driver註冊時候會在總線上搜索匹配的device來進行配對:
int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
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 || !bus->p)
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;
}
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
/*
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
*
* driver_probe_device() will spit a warning if there
* is an error.
*/
if (!driver_match_device(drv, dev))
return 0;
if (dev->parent) /* Needed for USB */
device_lock(dev->parent);
device_lock(dev);
if (!dev->driver)
driver_probe_device(drv, dev);
device_unlock(dev);
if (dev->parent)
device_unlock(dev->parent);
return 0;
}
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (!device_is_registered(dev))
return -ENODEV;
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
pm_runtime_barrier(dev);
ret = really_probe(dev, drv);
pm_request_idle(dev);
return ret;
}
可以看到和device匹配driver的過程是類似的,不再展開細說了。