• 【总结】设备树对platform平台设备驱动带来的变化(史上最强分析)【转】


    本文转载自:http://blog.csdn.net/fengyuwuzu0519/article/details/74375086

     

    目录(?)[+]

     

    最初我们学习设备树的时候,第一个例子是按键中断,其采用了设备树的方式。我们以此为例分析设备树引入对platform平台驱动的改变。

    tiny4412学习(四)之移植Linux-设备树(1)设备树基础知识及GPIO中断:http://blog.csdn.net/fengyuwuzu0519/article/details/74177978

    一、改变与不变

    (1)platform_driver的入口函数,仍采用platform_driver_register注册(不变)

    [cpp] view plain copy
     
     print?
    1. static int __init int_demo_init(void)    
    2. {    
    3.     int ret;    
    4.     
    5.     ret = platform_driver_register(&int_demo_driver);    
    6.     if (ret)    
    7.         printk(KERN_ERR "int demo: probe failed: %d ", ret);    
    8.     
    9.     return ret;    
    10. }    
    11. module_init(int_demo_init);    

    (2)平台驱动:稍微的变化,多了of_match_table成员
    [cpp] view plain copy
     
     print?
    1. static struct platform_driver int_demo_driver = {    
    2.     .driver        = {    
    3.         .name      = "interrupt_demo",    
    4.         .of_match_table    = of_match_ptr(int_demo_dt_ids),    
    5.     },    
    6.     .probe         = int_demo_probe,    
    7.     .remove        = int_demo_remove,    
    8. };    
    (3)匹配方式的变化:

    如果没有引入设备树,还需要定义类似以下文件来匹配

    [cpp] view plain copy
     
     print?
    1. static struct resource s3c_int_resource[] = {    
    2.     xxx;  
    3. };    
    4. struct platform_device s3c_device_rtc = {    
    5.     .name       = "interrupt_demo",    
    6.     .id     = -1,    
    7.     .num_resources  = ARRAY_SIZE(s3c_int_resource),    
    8.     .resource   = s3c_int_resource,    
    9. };    
    在Common-smdk.c (linux-3.4.2archarmmach-s3c24xx)里
    [cpp] view plain copy
     
     print?
    1. static struct platform_device __initdata *smdk_devs[] = {  
    2.     &s3c_device_nand,  
    3.     &smdk_led4,  
    4.     &smdk_led5,  
    5.     &smdk_led6,  
    6.     &smdk_led7,  
    7. };  
    8. //内核初始化时添加相应设备  
    9. 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函数。下面我们就来详细分析为什么是这样。

    [cpp] view plain copy
     
     print?
    1. static const struct of_device_id int_demo_dt_ids[] = {    
    2.     { .compatible = "tiny4412,interrupt_demo", },    
    3.     {},    
    4. };    
    5.     
    6. MODULE_DEVICE_TABLE(of, int_demo_dt_ids);    
    7.     
    8. static struct platform_driver int_demo_driver = {    
    9.     .driver        = {    
    10.         .name      = "interrupt_demo",    
    11.         .of_match_table    = of_match_ptr(int_demo_dt_ids),    
    12.     },    
    13.     .probe         = int_demo_probe,    
    14.     .remove        = int_demo_remove,    
    15. };    
    16.     
    17. static int __init int_demo_init(void)    
    18. {    
    19.     int ret;    
    20.     
    21.     ret = platform_driver_register(&int_demo_driver);    
    22.     if (ret)    
    23.         printk(KERN_ERR "int demo: probe failed: %d ", ret);    
    24.     
    25.     return ret;    
    26. }    
    27. module_init(int_demo_init);    
    完整代码见:http://blog.csdn.net/fengyuwuzu0519/article/details/74177978

    二、详细分析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

    [cpp] view plain copy
     
     print?
    1. static int platform_match(struct device *dev, struct device_driver *drv)  
    2. {  
    3.     struct platform_device *pdev = to_platform_device(dev);  
    4.     struct platform_driver *pdrv = to_platform_driver(drv);  
    5.   
    6.     /* Attempt an OF style match first */  
    7.     if (of_driver_match_device(dev, drv))  
    8.         return 1;  
    9.   
    10.     /* Then try to match against the id table */  
    11.     if (pdrv->id_table)  
    12.         return platform_match_id(pdrv->id_table, pdev) != NULL;  
    13.   
    14.     /* fall-back to driver name match */  
    15.     return (strcmp(pdev->name, drv->name) == 0);  
    16. }  

    of_driver_match_device(device,device_driver)

    [cpp] view plain copy
     
     print?
    1. static inline int of_driver_match_device(struct device *dev,  
    2.                      const struct device_driver *drv)  
    3. {  
    4.     return of_match_device(drv->of_match_table, dev) != NULL;  
    5. }  
    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:猜测是设备树构建的

    [cpp] view plain copy
     
     print?
    1. const struct of_device_id *of_match_device(const struct of_device_id *matches,  
    2.                        const struct device *dev)  
    3. {  
    4.     if ((!matches) || (!dev->of_node))  
    5.         return NULL;  
    6.     return of_match_node(matches, dev->of_node);  
    7. }  

    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的初始化)继续:

    [cpp] view plain copy
     
     print?
    1. const struct of_device_id *of_match_node(const struct of_device_id *matches,  
    2.                      const struct device_node *node)  
    3. {  
    4.     if (!matches)  
    5.         return NULL;  
    6.   
    7.     while (matches->name[0] || matches->type[0] || matches->compatible[0]) {  
    8.         int match = 1;  
    9.         if (matches->name[0])  
    10.             match &= node->name  
    11.                 && !strcmp(matches->name, node->name);  
    12.         if (matches->type[0])  
    13.             match &= node->type  
    14.                 && !strcmp(matches->type, node->type);  
    15.         if (matches->compatible[0])  
    16.             match &= of_device_is_compatible(node,  
    17.                         matches->compatible);  
    18.         if (match)  
    19.             return matches;  
    20.         matches++;  
    21.     }  
    22.     return NULL;  
    23. }  

    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成员做对比,那么是谁和它对比呢?我们继续看下一个函数:

    [cpp] view plain copy
     
     print?
    1. int of_device_is_compatible(const struct device_node *device,  
    2.         const char *compat)  
    3. {  
    4.     const char* cp;  
    5.     int cplen, l;  
    6.   
    7.     cp = of_get_property(device, "compatible", &cplen);  
    8.     if (cp == NULL)  
    9.         return 0;  
    10.     while (cplen > 0) {  
    11.         if (of_compat_cmp(cp, compat, strlen(compat)) == 0)  
    12.             return 1;  
    13.         l = strlen(cp) + 1;  
    14.         cp += l;  
    15.         cplen -= l;  
    16.     }  
    17.   
    18.     return 0;  
    19. }  

    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;

    [cpp] view plain copy
     
     print?
    1. const void *of_get_property(const struct device_node *np, const char *name,  
    2.              int *lenp)  
    3. {  
    4.     struct property *pp = of_find_property(np, name, lenp);  
    5.   
    6.     return pp ? pp->value : NULL;  
    7. }  
    of_compat_cmp:忽略大小写比较字符串。
    #define of_compat_cmp(s1, s2, l)strcasecmp((s1), (s2))

    3、相关结构体

    (1)device  Device.h (linux-3.4.2includelinux)
    [cpp] view plain copy
     
     print?
    1. <span style="font-size:14px;">struct device {  
    2.     struct device       *parent;  
    3.   
    4.     struct device_private   *p;  
    5.   
    6.     struct kobject kobj;  
    7.     const char      *init_name; /* initial name of the device */  
    8.     const struct device_type *type;  
    9.   
    10.     struct mutex        mutex;  /* mutex to synchronize calls to 
    11.                      * its driver. 
    12.                      */  
    13.   
    14.     struct bus_type *bus;       /* type of bus device is on */  
    15.     struct device_driver *driver;   /* which driver has allocated this 
    16.                        device */  
    17.     void        *platform_data; /* Platform specific data, device 
    18.                        core doesn't touch it */  
    19.     struct dev_pm_info  power;  
    20.     struct dev_pm_domain    *pm_domain;  
    21.   
    22. #ifdef CONFIG_NUMA  
    23.     int     numa_node;  /* NUMA node this device is close to */  
    24. #endif  
    25.     u64     *dma_mask;  /* dma mask (if dma'able device) */  
    26.     u64     coherent_dma_mask;/* Like dma_mask, but for 
    27.                          alloc_coherent mappings as 
    28.                          not all hardware supports 
    29.                          64 bit addresses for consistent 
    30.                          allocations such descriptors. */  
    31.   
    32.     struct device_dma_parameters *dma_parms;  
    33.   
    34.     struct list_head    dma_pools;  /* dma pools (if dma'ble) */  
    35.   
    36.     struct dma_coherent_mem *dma_mem; /* internal for coherent mem 
    37.                          override */  
    38.     /* arch specific additions */  
    39.     struct dev_archdata archdata;  
    40.   
    41.     struct device_node  *of_node; /* associated device tree node */  
    42.   
    43.     dev_t           devt;   /* dev_t, creates the sysfs "dev" */  
    44.     u32         id; /* device instance */  
    45.   
    46.     spinlock_t      devres_lock;  
    47.     struct list_head    devres_head;  
    48.   
    49.     struct klist_node   knode_class;  
    50.     struct class        *class;  
    51.     const struct attribute_group **groups;  /* optional groups */  
    52.   
    53.     void    (*release)(struct device *dev);  
    54. };</span>  
    (2)device_driver   Device.h (linux-3.4.2includelinux)
    [cpp] view plain copy
     
     print?
    1. struct device_driver {  
    2.     const char      *name;  
    3.     struct bus_type     *bus;  
    4.   
    5.     struct module       *owner;  
    6.     const char      *mod_name;  /* used for built-in modules */  
    7.   
    8.     bool suppress_bind_attrs;   /* disables bind/unbind via sysfs */  
    9.   
    10.     const struct of_device_id   *of_match_table;  
    11.   
    12.     int (*probe) (struct device *dev);  
    13.     int (*remove) (struct device *dev);  
    14.     void (*shutdown) (struct device *dev);  
    15.     int (*suspend) (struct device *dev, pm_message_t state);  
    16.     int (*resume) (struct device *dev);  
    17.     const struct attribute_group **groups;  
    18.   
    19.     const struct dev_pm_ops *pm;  
    20.   
    21.     struct driver_private *p;  
    22. };  


    三、总结

    到此我们知道了。是在比较驱动中我们定义的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我们后面再来分析。

  • 相关阅读:
    Python:如何将文件映射到内存
    关于系统中:/dev/mem
    Python:如何设置文件的缓冲
    Python:如何处理二进制文件
    Python:struct模块的pack、unpack
    Python:如何读写文本文件
    《鸟哥的Linux私房菜》读书笔记3
    《鸟哥的Linux私房菜》读书笔记2
    《鸟哥的Linux私房菜》读书笔记1
    raspberry是个什么玩意
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/7250010.html
Copyright © 2020-2023  润新知