linux 設備樹解析及probe調用流程


以platform設備為例

設備樹解析

很好的一張總結圖,轉自:設備樹解析過程及platform設備注冊

設備樹解析過程

probe函數調用

轉自:device 的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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM