本文轉載自:http://blog.csdn.net/fengyuwuzu0519/article/details/74375086
版權聲明:本文為博主原創文章,轉載請注明http://blog.csdn.net/fengyuwuzu0519。
最初我們學習設備樹的時候,第一個例子是按鍵中斷,其采用了設備樹的方式。我們以此為例分析設備樹引入對platform平台驅動的改變。
tiny4412學習(四)之移植Linux-設備樹(1)設備樹基礎知識及GPIO中斷:http://blog.csdn.net/fengyuwuzu0519/article/details/74177978
一、改變與不變
(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,
- };
如果沒有引入設備樹,還需要定義類似以下文件來匹配
- 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_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;
- }
#define of_compat_cmp(s1, s2, l)strcasecmp((s1), (s2))
3、相關結構體
- <span style="font-size:14px;">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);
- };</span>
- 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我們后面再來分析。