以platform設備為例
設備樹解析
很好的一張總結圖,轉自:設備樹解析過程及platform設備注冊
probe函數調用
在驅動程序中,我們通常會定義一個platform_driver的結構體,其中包含了各種操作函數。
static struct platform_driver xxx_driver = {
.probe = xxx_probe,
.remove = __devexit_p(xxx_remove),
.shutdown = xxx_shutdown,
.suspend = xxx_suspend,
.resume = xxx_resume,
.of_match_table = icm40607_of_match,
.driver = {
.owner = THIS_MODULE,
.name = "xxx_name",
.id_table = xxx_id,
},
};
其中,of_match_table和id_table都是用來在設備樹中和設備匹配,且都是通過compatible,前者優先級更高,后者則會在前者未匹配的情況下去掉compatible的供應商信息后再匹配,即id_table只匹配sensor名字。
而這個結構體的定義之中會找到許多函數指針。
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
struct platform_device_id *id_table;
};
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 */
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;
};
之后我們初始化時會調用函數platform_driver_register進行注冊,把platform的函數指針傳入了driver結構體中。這樣device_driver和platform_driver都有了具體的值,但是好像還是不知道如何執行到我們定義的probe函數。
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;
if (drv->probe)
drv->driver.probe = platform_drv_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);
}
繼續找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);
.
.
.
return drv->probe(dev);
}
找到to_platform_driver的定義后,發現調用了一個熟悉的函數container_of。
#define to_platform_driver(drv) \
(container_of((drv), struct platform_driver, driver))
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
container_of這個函數第一次遇見是再看宋寶華的書的時候,當時在寫platform總線下的字符設備驅動程序。但是,實現open函數時不知道怎么讓file的私有數據指針private_data指向我在probe中定義地設備結構體lxy_device的指針(局部變量)。
container_of(ptr,type,member)
,這里面有ptr,type,member分別代表傳入的具體成員指針、結構體類型、結構體中ptr對應的具體成員,ptr類型應該是typeof(member) *。
簡單說,這個函數可以根據結構體某個成員的地址獲取該結構體的指針(地址),最好能仔細理解這個函數,第一次見真是感覺很巧妙。
int driver_register(struct device_driver *drv)
{
...
ret = bus_add_driver(drv);
...
}
int bus_add_driver(struct device_driver *drv)
{
...
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
...
}
int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
//bus_for_each_dev就是對已經添加到drv->bus上面的每個設備操作。
int bus_for_each_dev(struct bus_type *bus, struct device *start, \
void *data, int (*fn)(struct device *, void *))
{
data = drv;
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);
}
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
if (!driver_match_device(drv, dev)) // here !
return 0;
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
down(&dev->sem);
if (!dev->driver)
driver_probe_device(drv, dev); //& here !
up(&dev->sem);
if (dev->parent)
up(&dev->parent->sem);
return 0;
}
driver_match_device 是檢測設備和驅動是否匹配;
drvier_probe_device 是執行probe函數。
//match 函數
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 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);
}
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
其實,bus_type中的match函數也就是platform_match函數,才是真正的將驅動和設備進行匹配的函數。如果成功就返回0. 看到了其中比較了 pdev->name 和 drv->name 。具體到watchdog的驅動中, 這兩個對應的就是字符串:"xxx_name"。
//probe 函數
在driver_probe_device中又調用了really_probe:
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
atomic_inc(&probe_count);
dev->driver = drv;
if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
goto probe_failed;
}
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->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;
參數dev來自:
__driver_attach中的device *dev
又來自於 next_device(&i)
i來自於klist_iter_init_node,應該是由設備樹匹配而來。
那么到了這一步,也就全部聯系起來了。
1、platform_driver_register注冊時把 platform_drv_probe賦值給drv->driver.probe。
2、然后driver_register調用了driver_probe_device,里面又調用了really_probe,里面通過drv->probe調用了drv->driver.probe,也就是調用了platform_drv_probe。其中傳入的參數是device *dev,dev_prv = to_device_private_bus,dev = dev_prv->device。猜測dev由設備樹匹配過來。
3、platform_drv_probe中又獲取了xxx_probe的指針,最后調用到我們編寫的xxx_probe。