【總結】設備樹對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\n", 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.2\arch\arm\mach-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\n", 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.2\include\linux)
[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.2\include\linux)
[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我們后面再來分析。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM