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我們後面再來分析。

相關推薦

總結裝置platform平臺裝置驅動帶來變化

最初我們學習裝置樹的時候,第一個例子是按鍵中斷,其採用了裝置樹的方式。我們以此為例分析裝置樹引入對platform平臺驅動的改變。 一、改變與不變 (1)platform_driver的入口函式,仍採用platform_driver_register註冊(不變) sta

總結 伸展Splay

挖坑待補 前言 感覺我在聯賽還差4天的時候學習Splay有點慌,但還是要學一下。 定義 我們先對Splay的陣列進行一些定義: struct node{ int ff,siz,cnt,ch[2],val; //ff表示父親,siz表示子樹大小,ch表示兒子,cnt表示與這個點權值相同

總結裝置語法及常用API函式

一、DTS編寫語法 二、常用函式 裝置樹函式思路是:uboot啟動時將裝置樹地址傳給核心,核心解析裝置樹,並建立裝置,初始化相關屬性,驅動中通過of_get_XXX函式去獲取裝置樹載入時建立的裝置。

總結異步處理的http接口進行性能測試

調研 內存 雲監控 後端 調優 系統 錯誤 手機 服務器 以前對接口做性能測試,接口都是同步處理的,請求之後等待響應結果就知道處理結果了,這樣只要看這個接口是否異常,如果無異常無報錯記錄這個接口的響應時間、TPS等性能指標進行分析就可以了,最近在工作中遇到了異步處理的接口,

演算法總結B+的實現

【參考資料】 【B+樹是什麼】 b+樹是b樹的變種。b+樹與b樹最大的不同在於:b+樹的關鍵字數量跟孩子節點數量一致,這與b樹不一樣。並且,b+樹的葉子節點包含有所有關鍵字及其對應資訊,非葉子節點只包含部分關鍵字(這部分關鍵字相當於邊界索引),不含具體資料,下面這幅圖就

演算法總結B總結

【參考資料】 【B樹解釋】 B樹是一棵樹,不同的是該樹的最多有m個子節點,至少floor(m/2)個子節點(根節點除外,根節點最少可以有兩個子節點。),但是裡面包含關鍵字數量比當前的子節點少1,在沒有子節點的情況下,關鍵字數量在 floor(m/2)-1到m-1之間。

Java整理的幾種遍歷方式總結

http://www.cnblogs.com/developerY/p/3323264.html BFS和DFS詳解以及java實現 http://sunlujing.iteye.com/blog/1876540 http://www.cnblogs.com/dolphin

P3372 模板線段 1

load color 求和 整數 數字 amp article http cst 題目描述 如題,已知一個數列,你需要進行下面兩種操作: 1.將某區間每一個數加上x 2.求出某區間每一個數的和 輸入輸出格式 輸入格式: 第一行包含兩個整數N、M,分別表示該數

Python決策的python實現

uia bmp say 不知道 times otto outlook lru bgm 【Python】決策樹的python實現 2016-12-08 數據分析師Nieson 1. 決策樹是什麽? 簡單地理解,就是根據一些 feature 進行分類,每個節點提一個問

bzoj2836魔法 鏈剖分+線段

urn fin pan online char font -s class efi 題目描述 輸入 輸出 樣例輸入 4 0 1 1 2 2 3 4 Add 1 3 1 Query 0 Query 1 Query 2 樣例輸出

[洛谷3373]模板線段 2

兩個 cstring tchar int() 維護 string max nbsp 線段 思路: 線段樹。同時維護兩個 lazy tag ,一個維護乘,一個維護加。根據加法結合律,可以得出:當同一個結點進行兩次加操作時,新的標記等於兩次標記之和。根據乘法結合律,可以得出:

2JVM-JAVA象的訪問

lin oar XML nts java棧 article value new string Java中對象的訪問 JAVA是面向對象的語言,那麽在JAVA虛擬機中,存在非常多的對象,對象訪問是無處不在的。即時是最簡單的訪問,也會涉及到JAVA棧、JAVA堆、方法區

P3373 模板線段 2 區間求和 區間乘 區間加

std 數列 cst printf int img ostream string uil 題目描述 如題,已知一個數列,你需要進行下面兩種操作: 1.將某區間每一個數加上x 2.將某區間每一個數乘上x 3.求出某區間每一個數的和 輸入輸出格式 輸入格

bzoj4785[Zjoi2017]狀數組 線段套線段

奇怪 原因 tdi function 數字 二進制位 操作 接下來 還需 題目描述 漆黑的晚上,九條可憐躺在床上輾轉反側。難以入眠的她想起了若幹年前她的一次悲慘的OI 比賽經歷。那是一道基礎的樹狀數組題。給出一個長度為 n 的數組 A,初始值都為 0,接下來進行 m 次操

BZOJ4785[Zjoi2017]狀數組 (二維線段

這也 現在 ont 平面 nbsp -s mil 比賽 turn 【BZOJ4785】[Zjoi2017]樹狀數組 Description 漆黑的晚上,九條可憐躺在床上輾轉反側。難以入眠的她想起了若幹年前她的一次悲慘的OI 比賽經歷。那是一道基礎的樹狀數組題。給出

Nginx教程(7) 正向代理與反向代理總結

資料 用戶訪問 認證 origin 訪問者 發送 -128 負載 行為 1、前言   最近工作中用到反向代理,發現網絡代理的玩法還真不少,網絡背後有很多需要去學習。而在此之前僅僅使用了過代理軟件,曾經為了訪問google,使用了代理軟件,需要在瀏覽器中配置代理的地址。我只知

線段區間修改 P3372 模板線段 1

print alt namespace clu 格式 getch 輸出格式 包含 模板    題目描述 如題,已知一個數列,你需要進行下面兩種操作: 1.將某區間每一個數加上x 2.求出某區間每一個數的和 輸入輸出格式 輸入格式: 第一行包含兩個整數N、M,分別表示該數

J2EE 關於response

狀態 nbsp har sta als edi 調用 stat 指定 兩種響應流   1.response.getWriter() //字符流     >默認為iso-8859-1,setCharactorEncoding("utf-8") \ setContent

原創洛谷 LUOGU P3373 模板線段2

取模 file 需要 code ace highlight dig org zh-cn P3373 【模板】線段樹 2 題目描述 如題,已知一個數列,你需要進行下面兩種操作: 1.將某區間每一個數加上x 2.將某區間每一個數乘上x 3.求出

總結spark按文本格式和Lzo格式處理Lzo壓縮文件的比較

spark lzotextinputformat1、描述spark中怎麽加載lzo壓縮格式的文件2、比較lzo格式文件以textFile方式和LzoTextInputFormat方式計算數據,Running Tasks個數的影響 a.確保lzo文件所在文件夾中生成lzo.index索引文件 b.以