關於platform_driver 是如何匹配 platform_device的和如何呼叫到platform_driver中的probe函式的研究
在linux中platform平臺驅動又三大部分組成,第一是bus、第二是驅動、第三是裝置。
第一匯流排也就是platform_bus,匯流排也是一種特殊的device,到底層下面還是要呼叫device_register來註冊該匯流排裝置,然後是用來註冊匯流排的屬性結構體 bus_type(platform_bus_type),至此platform平臺的匯流排已經準備好。具體介紹可以看我另一篇部落格。
第二是裝置,platform_device,它的註冊流程是:platform_device_register(struct platform_device *pdev)->device_initialize(&pdev->dev)->platform_device_add(pdev)->pdev->dev.bus = &platform_bus_type->device_add(&pdev->dev)->把裝置掛在虛擬的platform bus下。
第三是驅動,現在匯流排、裝置都準備好了,然後等著驅動來匹配裝置進行驅動,具體的驅動匹配裝置流程如下:
1、
[cpp] view plaincopyprint?- <SPAN style="FONT-SIZE: 16px">int platform_driver_register(struct platform_driver *drv)
- {
- drv->driver.bus = &platform_bus_type;//驅動的匯流排型別指向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;
- if (drv->suspend)
- drv->driver.suspend = platform_drv_suspend;
- if (drv->resume)
- drv->driver.resume = platform_drv_resume;
- return driver_register(&drv->driver);
- }</SPAN>
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;//驅動的匯流排型別指向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;
if (drv->suspend)
drv->driver.suspend = platform_drv_suspend;
if (drv->resume)
drv->driver.resume = platform_drv_resume;
return driver_register(&drv->driver);
}
2、
[cpp] view plaincopyprint?- <SPAN style="FONT-SIZE: 16px">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) {
- //如果已經被註冊,則返回提示錯誤並且減少引用計數。
- put_driver(other);
- printk(KERN_ERR "Error: Driver '%s' is already registered, "
- "aborting...\n", drv->name);
- return -EEXIST;
- }
- //若還沒有註冊,則在總線上註冊該驅動
- ret = bus_add_driver(drv);
- if (ret)
- return ret;
- ret = driver_add_groups(drv, drv->groups);
- if (ret)
- bus_remove_driver(drv);
- return ret;
- }</SPAN>
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) {
//如果已經被註冊,則返回提示錯誤並且減少引用計數。
put_driver(other);
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EEXIST;
}
//若還沒有註冊,則在總線上註冊該驅動
ret = bus_add_driver(drv);
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret)
bus_remove_driver(drv);
return ret;
}
3、
- <SPAN style="FONT-SIZE: 16px">int bus_add_driver(struct device_driver *drv)
- {
- struct bus_type *bus;
- struct driver_private *priv;
- int error = 0;
- //用於增加該bus所屬的頂層bus的kobject的引用計數,返回的是其所屬的頂層bus的指標。
- bus = bus_get(drv->bus);
- if (!bus)
- return -EINVAL;
- pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- error = -ENOMEM;
- goto out_put_bus;
- }
- klist_init(&priv->klist_devices, NULL, NULL);
- //將這兩個結構體連線起來
- priv->driver = drv;
- drv->p = priv;
- //指向頂層的bus的p->drivers_kset
- //設定私有資料的父容器,在這一步中,設定了kset為platform下的drivers_kset結構,也就是drivers呢個目錄
- priv->kobj.kset = bus->p->drivers_kset;
- //初始化kobj物件,設定容器操作集並建立相應的目錄,這裡由於沒有提供parent,所以會使用父容器中的kobj為父物件
- error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
- "%s", drv->name);
- if (error)
- goto out_unregister;
- //檢測所屬匯流排的drivers_autoprobe屬性是否為真
- //為真則進行與裝置的匹配,到這裡,就會與我們之前註冊的test_device連線上了,
- //至於如何連線,進行了什麼操作,將在別的文章中詳細描述
- if (drv->bus->p->drivers_autoprobe) {
- error = driver_attach(drv);
- if (error)
- goto out_unregister;
- }
- //掛載到所屬匯流排驅動連結串列上
- klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
- module_add_driver(drv->owner, drv);
- //建立uevent屬性檔案
- error = driver_create_file(drv, &driver_attr_uevent);
- if (error) {
- printk(KERN_ERR "%s: uevent attr (%s) failed\n",
- __func__, drv->name);
- }
- //建立裝置屬性檔案
- error = driver_add_attrs(bus, drv);
- if (error) {
- /* How the hell do we get out of this pickle? Give up */
- printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
- __func__, drv->name);
- }
- error = add_bind_files(drv);
- if (error) {
- /* Ditto */
- printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
- __func__, drv->name);
- }
- kobject_uevent(&priv->kobj, KOBJ_ADD);
- return 0;
- out_unregister:
- kfree(drv->p);
- drv->p = NULL;
- kobject_put(&priv->kobj);
- out_put_bus:
- bus_put(bus);
- return error;
- }</SPAN>
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
//用於增加該bus所屬的頂層bus的kobject的引用計數,返回的是其所屬的頂層bus的指標。
bus = bus_get(drv->bus);
if (!bus)
return -EINVAL;
pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
klist_init(&priv->klist_devices, NULL, NULL);
//將這兩個結構體連線起來
priv->driver = drv;
drv->p = priv;
//指向頂層的bus的p->drivers_kset
//設定私有資料的父容器,在這一步中,設定了kset為platform下的drivers_kset結構,也就是drivers呢個目錄
priv->kobj.kset = bus->p->drivers_kset;
//初始化kobj物件,設定容器操作集並建立相應的目錄,這裡由於沒有提供parent,所以會使用父容器中的kobj為父物件
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
if (error)
goto out_unregister;
//檢測所屬匯流排的drivers_autoprobe屬性是否為真
//為真則進行與裝置的匹配,到這裡,就會與我們之前註冊的test_device連線上了,
//至於如何連線,進行了什麼操作,將在別的文章中詳細描述
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
//掛載到所屬匯流排驅動連結串列上
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
module_add_driver(drv->owner, drv);
//建立uevent屬性檔案
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
__func__, drv->name);
}
//建立裝置屬性檔案
error = driver_add_attrs(bus, drv);
if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
__func__, drv->name);
}
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__func__, drv->name);
}
kobject_uevent(&priv->kobj, KOBJ_ADD);
return 0;
out_unregister:
kfree(drv->p);
drv->p = NULL;
kobject_put(&priv->kobj);
out_put_bus:
bus_put(bus);
return error;
}
4、驅動的匹配關鍵是上面函式中的
[cpp] view plaincopyprint?- <SPAN style="FONT-SIZE: 16px"> if (drv->bus->p->drivers_autoprobe) {//drivers_autoprobe在初始化的時候定義為1,系統則會呼叫下面的driver_attach函式進行驅動與裝置的匹配
- error = driver_attach(drv);
- if (error)
- goto out_unregister;
- }</SPAN>
if (drv->bus->p->drivers_autoprobe) {//drivers_autoprobe在初始化的時候定義為1,系統則會呼叫下面的driver_attach函式進行驅動與裝置的匹配
error = driver_attach(drv);
if (error)
goto out_unregister;
}
[cpp]
view plaincopyprint?
- <SPAN style="FONT-SIZE: 16px">int driver_attach(struct device_driver *drv)
- {
- return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
- }</SPAN>
int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
[cpp]
view plaincopyprint?
- <SPAN style="FONT-SIZE: 16px"> //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 {
- struct klist *i_klist;
- struct klist_node *i_cur;
- };
- */
- struct klist_iter i;
- struct device *dev;
- int error = 0;
- if (!bus)
- return -EINVAL;
- //如果第三個引數不為空就增加引用計數
- klist_iter_init_node(&bus->p->klist_devices, &i,
- (start ? &start->p->knode_bus : NULL));//一直是NULL
- //經過上面的巨集之後,i實際上變成了i->i_klist=&bus->p->klist_devices,bus->p->klist_devices指向的是掛接在它上面的所有的裝置的指標
- while ((dev = next_device(&i)) && !error)
- //fu函式傳入的是device的指標和device_driver的指標,
- error = fn(dev, data);
- //如果klist_iter_init_node第三個引數不為空則減少引用計數
- klist_iter_exit(&i);
- return error;
- }</SPAN>
//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 {
struct klist *i_klist;
struct klist_node *i_cur;
};
*/
struct klist_iter i;
struct device *dev;
int error = 0;
if (!bus)
return -EINVAL;
//如果第三個引數不為空就增加引用計數
klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));//一直是NULL
//經過上面的巨集之後,i實際上變成了i->i_klist=&bus->p->klist_devices,bus->p->klist_devices指向的是掛接在它上面的所有的裝置的指標
while ((dev = next_device(&i)) && !error)
//fu函式傳入的是device的指標和device_driver的指標,
error = fn(dev, data);
//如果klist_iter_init_node第三個引數不為空則減少引用計數
klist_iter_exit(&i);
return error;
}
//尋找到下一個裝置的節點
[cpp] view plaincopyprint?- <SPAN style="FONT-SIZE: 16px">static struct device *next_device(struct klist_iter *i)
- {
- struct klist_node *n = klist_next(i);
- struct device *dev = NULL;
- struct device_private *dev_prv;
- if (n) {
- dev_prv = to_device_private_bus(n);
- dev = dev_prv->device;
- }
- return dev;
- }</SPAN>
static struct device *next_device(struct klist_iter *i)
{
struct klist_node *n = klist_next(i);
struct device *dev = NULL;
struct device_private *dev_prv;
if (n) {
dev_prv = to_device_private_bus(n);
dev = dev_prv->device;
}
return dev;
}
函式error = fn(dev, data)的原型如下:
傳進來的引數第一個引數為不斷遍歷到的裝置節點的指標,第二個引數為固定的一個驅動所對應的struct device_driver *drv指標,這樣就實現驅動和裝置的匹配
[cpp] view plaincopyprint?- <SPAN style="FONT-SIZE: 16px">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.
- */
- //當裝置和驅動的名字不匹配的時候返回的是0,然後就會呼叫下面的return 0;
- if (!driver_match_device(drv, dev))
- return 0;
- if (dev->parent) /* Needed for USB */
- down(&dev->parent->sem);
- down(&dev->sem);
- if (!dev->driver)
- driver_probe_device(drv, dev);//呼叫探測函式進行探測,並且呼叫platform_driver中的probe函式
- up(&dev->sem);
- if (dev->parent)
- up(&dev->parent->sem);
- return 0;
- }
- </SPAN>
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.
*/
//當裝置和驅動的名字不匹配的時候返回的是0,然後就會呼叫下面的return 0;
if (!driver_match_device(drv, dev))
return 0;
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
down(&dev->sem);
if (!dev->driver)
driver_probe_device(drv, dev);//呼叫探測函式進行探測,並且呼叫platform_driver中的probe函式
up(&dev->sem);
if (dev->parent)
up(&dev->parent->sem);
return 0;
}
在上面有兩個比較關鍵的函式driver_match_device(dre,dev),函式原型如下:
- <SPAN style="FONT-SIZE: 16px">static inline int driver_match_device(struct device_driver *drv,
- struct device *dev)
- {
- return drv->bus->match ? drv->bus->match(dev, drv) : 1;//無論裝置與驅動是否匹配成功都會返回1
- }</SPAN>
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;//無論裝置與驅動是否匹配成功都會返回1
}
當驅動的指標不為空的時候,這個drv->bus所指向的的這個驅動所屬的的匯流排的bus_type中的match函式,然後傳進去的是該驅動的指標和裝置的指標
[cpp] view plaincopyprint?- <SPAN style="FONT-SIZE: 16px">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);
- /* match against the id table first */
- 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);
- }</SPAN>
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);
/* match against the id table first */
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);
}
最後呼叫驅動的probe函式進行裝置的探測
driver_probe_device(drv, dev);//呼叫探測函式進行探測,並且呼叫platform_driver中的probe函式
[cpp] view plaincopyprint?- <SPAN style="FONT-SIZE: 16px">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);
- ret = really_probe(dev, drv);
- return ret;
- }</SPAN>
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);
ret = really_probe(dev, drv);
return ret;
}
[cpp]
view plaincopyprint?
- <SPAN style="FONT-SIZE: 16px">static int really_probe(struct device *dev, struct device_driver *drv)
- {
- int ret = 0;
- atomic_inc(&probe_count);
- pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
- drv->bus->name, __func__, drv->name, dev_name(dev));
- WARN_ON(!list_empty(&dev->devres_head));
- //找到了裝置的驅動,並且將dev->driver指標指向自己的這個驅動
- dev->driver = drv;
- if (driver_sysfs_add(dev)) {//在sys目錄下建立連線指向自己的在sys中的drivers
- printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
- __func__, dev_name(dev));
- goto probe_failed;
- }
- //在bus_type platform_bus_type中並沒有設定probe函式,所以下面函式並不會被呼叫
- if (dev->bus->probe) {
- ret = dev->bus->probe(dev);
- if (ret)
- goto probe_failed;
- } else if (drv->probe) {//上面匯流排沒有probe函式,所以直接呼叫驅動當中的probe函式
- ret = drv->probe(dev);
- if (ret)
- goto probe_failed;
- }
- driver_bound(dev);
- ret = 1;
- pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
- drv->bus->name, __func__, dev_name(dev), drv->name);
- goto done;
- probe_failed:
- devres_release_all(dev);
- driver_sysfs_remove(dev);
- dev->driver = NULL;
- if (ret != -ENODEV && ret != -ENXIO) {
- /* driver matched but the probe failed */
- printk(KERN_WARNING
- "%s: probe of %s failed with error %d\n",
- drv->name, dev_name(dev), ret);
- }
- /*
- * Ignore errors returned by ->probe so that the next driver can try
- * its luck.
- */
- ret = 0;
- done:
- atomic_dec(&probe_count);
- wake_up(&probe_waitqueue);
- return ret;
- }</SPAN>
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
atomic_inc(&probe_count);
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
drv->bus->name, __func__, drv->name, dev_name(dev));
WARN_ON(!list_empty(&dev->devres_head));
//找到了裝置的驅動,並且將dev->driver指標指向自己的這個驅動
dev->driver = drv;
if (driver_sysfs_add(dev)) {//在sys目錄下建立連線指向自己的在sys中的drivers
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
goto probe_failed;
}
//在bus_type platform_bus_type中並沒有設定probe函式,所以下面函式並不會被呼叫
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {//上面匯流排沒有probe函式,所以直接呼叫驅動當中的probe函式
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
driver_bound(dev);
ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;
probe_failed:
devres_release_all(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
if (ret != -ENODEV && ret != -ENXIO) {
/* driver matched but the probe failed */
printk(KERN_WARNING
"%s: probe of %s failed with error %d\n",
drv->name, dev_name(dev), ret);
}
/*
* Ignore errors returned by ->probe so that the next driver can try
* its luck.
*/
ret = 0;
done:
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
}
至此platform_driver的probe函式實現了呼叫並且匹配了platform_device