本文转载自: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注册(不变)

  1. static int __init int_demo_init(void)
  2. {
  3. int ret;
  4. ret = platform_driver_register(&int_demo_driver);
  5. if (ret)
  6. printk(KERN_ERR "int demo: probe failed: %d\n", ret);
  7. return ret;
  8. }
  9. module_init(int_demo_init);

(2)平台驱动:稍微的变化,多了of_match_table成员

  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)匹配方式的变化:

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

  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)里

  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函数。下面我们就来详细分析为什么是这样。

  1. static const struct of_device_id int_demo_dt_ids[] = {
  2. { .compatible = "tiny4412,interrupt_demo", },
  3. {},
  4. };
  5. MODULE_DEVICE_TABLE(of, int_demo_dt_ids);
  6. static struct platform_driver int_demo_driver = {
  7. .driver        = {
  8. .name      = "interrupt_demo",
  9. .of_match_table    = of_match_ptr(int_demo_dt_ids),
  10. },
  11. .probe         = int_demo_probe,
  12. .remove        = int_demo_remove,
  13. };
  14. static int __init int_demo_init(void)
  15. {
  16. int ret;
  17. ret = platform_driver_register(&int_demo_driver);
  18. if (ret)
  19. printk(KERN_ERR "int demo: probe failed: %d\n", ret);
  20. return ret;
  21. }
  22. 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

  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. /* Attempt an OF style match first */
  6. if (of_driver_match_device(dev, drv))
  7. return 1;
  8. /* Then try to match against the id table */
  9. if (pdrv->id_table)
  10. return platform_match_id(pdrv->id_table, pdev) != NULL;
  11. /* fall-back to driver name match */
  12. return (strcmp(pdev->name, drv->name) == 0);
  13. }

of_driver_match_device(device,device_driver)

  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:猜测是设备树构建的

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

  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. while (matches->name[0] || matches->type[0] || matches->compatible[0]) {
  7. int match = 1;
  8. if (matches->name[0])
  9. match &= node->name
  10. && !strcmp(matches->name, node->name);
  11. if (matches->type[0])
  12. match &= node->type
  13. && !strcmp(matches->type, node->type);
  14. if (matches->compatible[0])
  15. match &= of_device_is_compatible(node,
  16. matches->compatible);
  17. if (match)
  18. return matches;
  19. matches++;
  20. }
  21. return NULL;
  22. }

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

  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. cp = of_get_property(device, "compatible", &cplen);
  7. if (cp == NULL)
  8. return 0;
  9. while (cplen > 0) {
  10. if (of_compat_cmp(cp, compat, strlen(compat)) == 0)
  11. return 1;
  12. l = strlen(cp) + 1;
  13. cp += l;
  14. cplen -= l;
  15. }
  16. return 0;
  17. }

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;

  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. return pp ? pp->value : NULL;
  6. }

of_compat_cmp:忽略大小写比较字符串。

#define of_compat_cmp(s1, s2, l)strcasecmp((s1), (s2))

3、相关结构体

(1)device  Device.h (linux-3.4.2\include\linux)
  1. <span style="font-size:14px;">struct device {
  2. struct device       *parent;
  3. struct device_private   *p;
  4. struct kobject kobj;
  5. const char      *init_name; /* initial name of the device */
  6. const struct device_type *type;
  7. struct mutex        mutex;  /* mutex to synchronize calls to
  8. * its driver.
  9. */
  10. struct bus_type *bus;       /* type of bus device is on */
  11. struct device_driver *driver;   /* which driver has allocated this
  12. device */
  13. void        *platform_data; /* Platform specific data, device
  14. core doesn't touch it */
  15. struct dev_pm_info  power;
  16. struct dev_pm_domain    *pm_domain;
  17. #ifdef CONFIG_NUMA
  18. int     numa_node;  /* NUMA node this device is close to */
  19. #endif
  20. u64     *dma_mask;  /* dma mask (if dma'able device) */
  21. u64     coherent_dma_mask;/* Like dma_mask, but for
  22. alloc_coherent mappings as
  23. not all hardware supports
  24. 64 bit addresses for consistent
  25. allocations such descriptors. */
  26. struct device_dma_parameters *dma_parms;
  27. struct list_head    dma_pools;  /* dma pools (if dma'ble) */
  28. struct dma_coherent_mem *dma_mem; /* internal for coherent mem
  29. override */
  30. /* arch specific additions */
  31. struct dev_archdata archdata;
  32. struct device_node  *of_node; /* associated device tree node */
  33. dev_t           devt;   /* dev_t, creates the sysfs "dev" */
  34. u32         id; /* device instance */
  35. spinlock_t      devres_lock;
  36. struct list_head    devres_head;
  37. struct klist_node   knode_class;
  38. struct class        *class;
  39. const struct attribute_group **groups;  /* optional groups */
  40. void    (*release)(struct device *dev);
  41. };</span>

(2)device_driver   Device.h (linux-3.4.2\include\linux)

  1. struct device_driver {
  2. const char      *name;
  3. struct bus_type     *bus;
  4. struct module       *owner;
  5. const char      *mod_name;  /* used for built-in modules */
  6. bool suppress_bind_attrs;   /* disables bind/unbind via sysfs */
  7. const struct of_device_id   *of_match_table;
  8. int (*probe) (struct device *dev);
  9. int (*remove) (struct device *dev);
  10. void (*shutdown) (struct device *dev);
  11. int (*suspend) (struct device *dev, pm_message_t state);
  12. int (*resume) (struct device *dev);
  13. const struct attribute_group **groups;
  14. const struct dev_pm_ops *pm;
  15. struct driver_private *p;
  16. };

三、总结

到此我们知道了。是在比较驱动中我们定义的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平台设备驱动带来的变化(史上最强分析)【转】的更多相关文章

  1. platform平台设备驱动简化示例代码

    driver.c: #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h& ...

  2. 设备树..ing

    .dts==>.dtb ==>device_node ==>  platform_device ==> led_dev.c  ==>匹配 led_drv.c    (设备 ...

  3. 使用设备树来编写led驱动程序

    在总线设备驱动模型中,平台设备是写在c文件中.使用设备树时,平台设备事先并不存在,在dts文件中构造节点,节点里面含有资源.dts文件被编译成dtb文件,然后传递给内核.内核会解析dtb文件,得到一个 ...

  4. Linux设备驱动模型之platform(平台)总线详解

    /********************************************************/ 内核版本:2.6.35.7 运行平台:三星s5pv210 /*********** ...

  5. Linux Platform devices 平台设备驱动

    设备总线驱动模型:http://blog.csdn.net/lizuobin2/article/details/51570196 本文主要参考:http://www.wowotech.net/devi ...

  6. 字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联

    转载自:http://www.kancloud.cn/yueqian_scut/emlinux/106829 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sy ...

  7. linux设备驱动那点事儿之平台设备理论篇

    一:Platform总线 1.1概述 一个现实的linux设备驱动通常需要挂接在一种总线上,对于本身依附于PCI,USB,IIC,SPI等的设备而言,这自然不是问题,但是在嵌入式系统里面,SOC系统中 ...

  8. Linux驱动之平台设备驱动模型简析(驱动分离分层概念的建立)

    Linux设备模型的目的:为内核建立一个统一的设备模型,从而有一个对系统结构的一般性抽象描述.换句话说,Linux设备模型提取了设备操作的共同属性,进行抽象,并将这部分共同的属性在内核中实现,而为需要 ...

  9. [kernel]字符设备驱动、平台设备驱动、设备驱动模型、sysfs几者之间的比较和关联

    转自:http://www.2cto.com/kf/201510/444943.html Linux驱动开发经验总结,绝对干货! 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动 ...

随机推荐

  1. POJ_2536_Gopher II

    题意:n只地鼠,m个地鼠洞,地鼠必须以v的速度在s秒内钻进洞且每个洞仅能容纳一只地鼠,问最少有几只地鼠会被老鹰吃掉. 分析:最大匹配问题,将s秒内地鼠能够跑到的洞与该地鼠连成一条边,在最后得到的图中使 ...

  2. java项目其他基础配置

    创建完maven项目之后. 1.pom.xml文件配置项目相关的架包. 2.src.main.resources下边 创建文件夹:spring以及mapper. 3.src.main.resource ...

  3. docker常用命令理解

    docker help Commands: attach Attach local standard input, output, and error streams to a running con ...

  4. 03Microsoft SQL Server 数据类型

    Microsoft SQL Server 数据类型 数据类型 Number 类型: 数据类型 描述 存储 bit  允许0,1或NULL    tinyint 允许从 0 到 255 的所有数字. 1 ...

  5. PrintWriter与ServletOutputStream的区别之文件下载

    copy自:https://blog.csdn.net/weixin_37703598/article/details/803870611.out = response.getWriter(); re ...

  6. console.log格式化及console对象

    一.console.log格式化打印 console.log格式化这一用法一般都在个人博客或其他官网上有,当F12查看网页元素时,在控制台(console)那里偶尔会发现一些个性化的输出,感觉很奇特很 ...

  7. P1036 选数(DFS)

    题目 https://www.luogu.org/problemnew/show/P1036 思路 搜索,使用递归实现dfs,所有数字遍历一遍,当取遍所有数组的index(扫了一遍,并非一定是选取了) ...

  8. 迷宫问题 POJ - 3984 (搜索输出路径)

    题目大意 题目不需要大意,poj居然还有中文题 鸣谢 特别鸣谢ljc大佬提供的方法!!! 解法 我们可能输出个最短路径的长度比较简单,但是输出最短路径真的是没有做过,这里有一种简单的方法 因为我们的d ...

  9. hdu 1754 I Hate It(线段树水题)

    >>点击进入原题测试<< 思路:线段树水题,可以手敲 #include<string> #include<iostream> #include<a ...

  10. [bzoj3209][花神的数论题] (数位dp+费马小定理)

    Description 背景众所周知,花神多年来凭借无边的神力狂虐各大 OJ.OI.CF.TC …… 当然也包括 CH 啦.描述话说花神这天又来讲课了.课后照例有超级难的神题啦…… 我等蒟蒻又遭殃了. ...