【總結】裝置樹對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成員
(3)匹配方式的變化: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, };
如果沒有引入裝置樹,還需要定義類似以下檔案來匹配
在Common-smdk.c (linux-3.4.2\arch\arm\mach-s3c24xx)裡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, };
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)
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”)
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我們後面再來分析。