1. 程式人生 > >【總結】裝置樹對platform平臺裝置驅動帶來的變化

【總結】裝置樹對platform平臺裝置驅動帶來的變化

最初我們學習裝置樹的時候,第一個例子是按鍵中斷,其採用了裝置樹的方式。我們以此為例分析裝置樹引入對platform平臺驅動的改變

一、改變與不變

(1)platform_driver的入口函式,仍採用platform_driver_register註冊(不變)

static int __init int_demo_init(void)  
{  
    int ret;  
  
    ret = platform_driver_register(&int_demo_driver);  
    if (ret)  
        printk(KERN_ERR "int demo: probe failed: %d\n", ret);  
  
    return ret;  
}  
module_init(int_demo_init);  

(2)平臺驅動:稍微的變化,多了of_match_table成員
static struct platform_driver int_demo_driver = {  
    .driver        = {  
        .name      = "interrupt_demo",  
        .of_match_table    = of_match_ptr(int_demo_dt_ids),  
    },  
    .probe         = int_demo_probe,  
    .remove        = int_demo_remove,  
};  
(3)匹配方式的變化:

如果沒有引入裝置樹,還需要定義類似以下檔案來匹配

	static struct resource s3c_int_resource[] = {  
	    xxx;
	};  
	struct platform_device s3c_device_rtc = {  
	    .name       = "interrupt_demo",  
	    .id     = -1,  
	    .num_resources  = ARRAY_SIZE(s3c_int_resource),  
     .resource   = s3c_int_resource,  
	};  
在Common-smdk.c (linux-3.4.2\arch\arm\mach-s3c24xx)裡
static struct platform_device __initdata *smdk_devs[] = {
	&s3c_device_nand,
	&smdk_led4,
	&smdk_led5,
	&smdk_led6,
	&smdk_led7,
};
//核心初始化時新增相應裝置
platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs));

沒有引入裝置樹之前,我們採用裝置名字匹配的方式,當platform_driver_register的時候,會去匹配一個名字為"interrupt_demo"的裝置,如果找到同名裝置則呼叫probe函式。由於裝置樹的引入,被硬編碼在arch/arm/plat-xxx和arch/arm/mach-xxx,比如板上的platform裝置、resource、i2c_board_info、spi_board_info以及各種硬體的platform_data將不存在。那麼這些裝置資訊在哪裡,什麼時候被add進核心,platform_driver如何匹配platform_device呢?答案是裝置資訊存在裝置樹中,裝置樹載入的時候被轉換成裝置結構體。platform不在像以前那樣匹配裝置名字,而是匹配驅動中的.compatible與裝置樹中相應節點的compatible屬性是否一致,且不區分大小寫。一致則呼叫probe函式。下面我們就來詳細分析為什麼是這樣。

static const struct of_device_id int_demo_dt_ids[] = {  
    { .compatible = "tiny4412,interrupt_demo", },  
    {},  
};  
  
MODULE_DEVICE_TABLE(of, int_demo_dt_ids);  
  
static struct platform_driver int_demo_driver = {  
    .driver        = {  
        .name      = "interrupt_demo",  
        .of_match_table    = of_match_ptr(int_demo_dt_ids),  
    },  
    .probe         = int_demo_probe,  
    .remove        = int_demo_remove,  
};  
  
static int __init int_demo_init(void)  
{  
    int ret;  
  
    ret = platform_driver_register(&int_demo_driver);  
    if (ret)  
        printk(KERN_ERR "int demo: probe failed: %d\n", ret);  
  
    return ret;  
}  
module_init(int_demo_init);  

二、詳細分析platform_match的過程

1、函式呼叫流程:

去核心裡檢視,便可發現一層一層是這麼呼叫的。

platform_match-->of_driver_match_device-->of_match_device-->of_match_node-->of_device_is_compatible-->of_get_property/of_compat_cmp-->strcasecmp((s1), (s2))

我們發現最後是在比較字串內容一否一致,所以我們只需要分析這幾個方法的成員列表,看到底比較的是哪兩個字串即可。

2、方法分析

platform_driver_register,首先呼叫到如下匹配函式。

platform_match(device,device_driver)

device:猜測是裝置樹構建的

device_driver:被platform_driver封裝,就是我們的int_demo_driver

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 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);
}

of_driver_match_device(device,device_driver)

static inline int of_driver_match_device(struct device *dev,
					 const struct device_driver *drv)
{
	return of_match_device(drv->of_match_table, dev) != NULL;
}
of_match_device(of_device_id,device)

of_device_id:device_driver>of_match_table=of_match_ptr(int_demo_dt_ids):這個不就是我們在驅動裡面定義的of_match_table成員

device:猜測是裝置樹構建的

const struct of_device_id *of_match_device(const struct of_device_id *matches,
					   const struct device *dev)
{
	if ((!matches) || (!dev->of_node))
		return NULL;
	return of_match_node(matches, dev->of_node);
}

of_match_node(of_device_id,device_node)

of_device_id:of_match_ptr(int_demo_dt_ids)

device_node:device->of_node(裝置樹完成了of_node的初始化)繼續:

const struct of_device_id *of_match_node(const struct of_device_id *matches,
					 const struct device_node *node)
{
	if (!matches)
		return NULL;

	while (matches->name[0] || matches->type[0] || matches->compatible[0]) {
		int match = 1;
		if (matches->name[0])
			match &= node->name
				&& !strcmp(matches->name, node->name);
		if (matches->type[0])
			match &= node->type
				&& !strcmp(matches->type, node->type);
		if (matches->compatible[0])
			match &= of_device_is_compatible(node,
						matches->compatible);
		if (match)
			return matches;
		matches++;
	}
	return NULL;
}

of_device_is_compatible(device_node,char *compat)=of_device_is_compatible(device_node,“tiny4412,interrupt_demo”)

device_node:device->of_node(裝置樹完成了of_node的初始化)

char *compat:of_device_id->compatible=tiny4412,interrupt_demo

到此我們已經可以發現 ,現在是在和驅動裡面定義的of_device_id結構體的compatible成員做對比,那麼是誰和它對比呢?我們繼續看下一個函式:

int of_device_is_compatible(const struct device_node *device,
		const char *compat)
{
	const char* cp;
	int cplen, l;

	cp = of_get_property(device, "compatible", &cplen);
	if (cp == NULL)
		return 0;
	while (cplen > 0) {
		if (of_compat_cmp(cp, compat, strlen(compat)) == 0)
			return 1;
		l = strlen(cp) + 1;
		cp += l;
		cplen -= l;
	}

	return 0;
}

cp = of_get_property(device_node,"compatible", &cplen)

device_node:device->of_node(裝置樹完成了of_node的初始化)

裝置樹載入的時候構建了device裝置,被初始化了of_node成員,現在我們根據of_node去獲取節點對應的compatible屬性。cp就等於裝置樹裡我們定義的節點的compatible屬性值。如上函式of_device_is_compatible,則對比了裝置樹中節點的compatible與我們定義的是否存在名字一致的裝置。存在則返回1;

const void *of_get_property(const struct device_node *np, const char *name,
			 int *lenp)
{
	struct property *pp = of_find_property(np, name, lenp);

	return pp ? pp->value : NULL;
}
of_compat_cmp:忽略大小寫比較字串。
#define of_compat_cmp(s1, s2, l)strcasecmp((s1), (s2))

3、相關結構體

(1)device  Device.h (linux-3.4.2\include\linux)
struct device {
	struct device		*parent;

	struct device_private	*p;

	struct kobject kobj;
	const char		*init_name; /* initial name of the device */
	const struct device_type *type;

	struct mutex		mutex;	/* mutex to synchronize calls to
					 * its driver.
					 */

	struct bus_type	*bus;		/* type of bus device is on */
	struct device_driver *driver;	/* which driver has allocated this
					   device */
	void		*platform_data;	/* Platform specific data, device
					   core doesn't touch it */
	struct dev_pm_info	power;
	struct dev_pm_domain	*pm_domain;

#ifdef CONFIG_NUMA
	int		numa_node;	/* NUMA node this device is close to */
#endif
	u64		*dma_mask;	/* dma mask (if dma'able device) */
	u64		coherent_dma_mask;/* Like dma_mask, but for
					     alloc_coherent mappings as
					     not all hardware supports
					     64 bit addresses for consistent
					     allocations such descriptors. */

	struct device_dma_parameters *dma_parms;

	struct list_head	dma_pools;	/* dma pools (if dma'ble) */

	struct dma_coherent_mem	*dma_mem; /* internal for coherent mem
					     override */
	/* arch specific additions */
	struct dev_archdata	archdata;

	struct device_node	*of_node; /* associated device tree node */

	dev_t			devt;	/* dev_t, creates the sysfs "dev" */
	u32			id;	/* device instance */

	spinlock_t		devres_lock;
	struct list_head	devres_head;

	struct klist_node	knode_class;
	struct class		*class;
	const struct attribute_group **groups;	/* optional groups */

	void	(*release)(struct device *dev);
};
(2)device_driver   Device.h (linux-3.4.2\include\linux)
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;

	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;
};


三、總結

到此我們知道了。是在比較驅動中我們定義的of_device_id型別的結構體裡面的compatible名字與裝置樹節點的compatible來決定是否執行probe函式。我們並沒有初始化platform_device,這些是核心載入裝置樹的時候幫我們完成的,並且根據裝置樹節點初始化了of_node成員,我們可以根據of_node找到節點對應的成員屬性。即裝置樹載入之後,核心會自動把裝置樹節點轉換成 platform_device這種格式,同時把名字放到of_node這個地方。

還有一點我們上面用到的結構體是device,和device_driver,為什麼不是我們定義的platform_device和platform_driver呢?其實platform是對device的一層封裝,檢視原始碼我們就可以發現函式呼叫流程:

platform_device--》device            platform_device_register  --》device_add
 platform_driver--》device_driver        platform_driver_register--》device_register
所以platform是對struct device和struct device_driver的封裝。

對於device和device_driver我們後面再來分析。

相關推薦

no