【总结】设备树对platform平台设备驱动带来的变化(史上最强分析)【转】
本文转载自: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,
- };
(3)匹配方式的变化:
如果没有引入设备树,还需要定义类似以下文件来匹配
- 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,
- };
在Common-smdk.c (linux-3.4.2\arch\arm\mach-s3c24xx)里
- 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);
完整代码见: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
- 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)
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;
- }
of_compat_cmp:忽略大小写比较字符串。
#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>
(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我们后面再来分析。
【总结】设备树对platform平台设备驱动带来的变化(史上最强分析)【转】的更多相关文章
- platform平台设备驱动简化示例代码
driver.c: #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h& ...
- 设备树..ing
.dts==>.dtb ==>device_node ==> platform_device ==> led_dev.c ==>匹配 led_drv.c (设备 ...
- 使用设备树来编写led驱动程序
在总线设备驱动模型中,平台设备是写在c文件中.使用设备树时,平台设备事先并不存在,在dts文件中构造节点,节点里面含有资源.dts文件被编译成dtb文件,然后传递给内核.内核会解析dtb文件,得到一个 ...
- Linux设备驱动模型之platform(平台)总线详解
/********************************************************/ 内核版本:2.6.35.7 运行平台:三星s5pv210 /*********** ...
- Linux Platform devices 平台设备驱动
设备总线驱动模型:http://blog.csdn.net/lizuobin2/article/details/51570196 本文主要参考:http://www.wowotech.net/devi ...
- 字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联
转载自:http://www.kancloud.cn/yueqian_scut/emlinux/106829 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sy ...
- linux设备驱动那点事儿之平台设备理论篇
一:Platform总线 1.1概述 一个现实的linux设备驱动通常需要挂接在一种总线上,对于本身依附于PCI,USB,IIC,SPI等的设备而言,这自然不是问题,但是在嵌入式系统里面,SOC系统中 ...
- Linux驱动之平台设备驱动模型简析(驱动分离分层概念的建立)
Linux设备模型的目的:为内核建立一个统一的设备模型,从而有一个对系统结构的一般性抽象描述.换句话说,Linux设备模型提取了设备操作的共同属性,进行抽象,并将这部分共同的属性在内核中实现,而为需要 ...
- [kernel]字符设备驱动、平台设备驱动、设备驱动模型、sysfs几者之间的比较和关联
转自:http://www.2cto.com/kf/201510/444943.html Linux驱动开发经验总结,绝对干货! 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动 ...
随机推荐
- Application crashes -程序崩溃原因
Typical errors that result in application crashes include: attempting to read or write memory that i ...
- map 用法
map 是一种关联容器, 提供一对一的关联, 关联的形式为: KEY----VALUE 关键字不重复.multimap与map类似,但是允许关键字重复 即:关键字和与之对应的值 关键字起到索 ...
- java虚拟机(三)--HotSpot 对象
普通对象的创建(不包括数组和class对象): 当虚拟机遇到new指令时,会在常量池中检查是否包含这个类的符号引用(全限定名),通过这个确定是否经过类加载的过程,如果true,为该 对象分配内存,对象 ...
- 04Microsoft SQL Server 数据库创建,查看,使用,修改及删除
Microsoft SQL Server 数据库创建,查看,使用,修改及删除 创建数据库 创建普通数据库 USE [master] GO CREATE DATABASE [MyDataBase] -- ...
- Linux命令rsync使用总结
详细用法见:https://www.cnblogs.com/oboth-zl/articles/10334754.html rsync命令简介 主要用于数据同步.备份和镜像,除了本地使用之外,也可以通 ...
- mysql连接错误解决(ERROR 2049 (HY000): Connection using old (pre-4.1.1) authentication protocol ref used (client option 'secure_auth' enabled))
当使用mysql的新版本是,连接老版本的mysql,就会有可能报: ERROR 2049 (HY000): Connection using old (pre-4.1.1) authenticatio ...
- 【OpenCV, MFC】利用MFC和OpenCV通过系统对话框打开和保存图片
打开图片: void CImageProDlg::OnImageopen() { // TODO: 在此添加命令处理程序代码 Invalidate(); CFileDialog dlg(TRUE, N ...
- 【Codeforces 584C】Marina and Vasya
[链接] 我是链接,点我呀:) [题意] 题意 [题解] 设cnt表示s1和s2不同的字符的个数 如果cnt>2t 因为这cnt个位置肯定至少有一边不同 显然肯定会有一个f(s,S)的值大于t的 ...
- JavaSE 学习笔记之网络编程(二十三)
端口: 物理端口: 逻辑端口:用于标识进程的逻辑地址,不同进程的标识:有效端口:0~65535,其中0~1024系统使用或保留端口. java 中ip对象:InetAddress. import ja ...
- P3383 【模板】线性筛素数 洛谷
https://www.luogu.org/problem/show?pid=3383#sub 题目描述 如题,给定一个范围N,你需要处理M个某数字是否为质数的询问(每个数字均在范围1-N内) 输入输 ...