Linux设备管理的简单分析

首先给出几个常见概念:class、bus、device、device driver、platform_device、platform_driver

系统中,关系简图如下(从总线bus的角度分析)

可以看到device和device driver必须依附于某一种总线。就是说总线下面挂着一些已经注册过的设备,一个设备对应着一个设备驱动。

如果从类别class的角度分析,这些设备就会被分为具体的设备,比如:声卡、网卡、输入设备等。

在目录/sys下,class、bus、device之间的关系如下图:

在linux中,有一种虚拟总线叫做platform,Linux2.6版本以后的设备驱动,需要关心总线,设备,驱动这3个实体。总线将设备和驱动绑定。系统每注册一个设备的时候,会寻找与之匹配的驱动;系统每注册一个驱动的时候,会寻找与之匹配的设备。

一个现实的linux设备和驱动通常都需要挂接在一个总线上,对于本身依附于PCI,USB,I2C等设备,挂接在总线上自然不是问题。但是对于嵌入式系统,SOC集成了独立外设控制器,挂接在SOC内存空间的外设等却不依附于此类总线。基于此背景,linux发明了一种虚拟总线——platform总线,相应的设备成为platform_device,驱动称为platform_driver。

设备或者驱动注册的时候都会触发总线调用match函数来寻找目前总线是否挂载有与该设备(或驱动)名字匹配的驱动(或设备),如果存在则将双方绑定。

简单分析就到此,后续还有对设备管理的深入分析。

Linux系统解析dts

Linux系统需要根据设备树填充如下结构体:

  1. struct property {
  2. char *name; /* property full name */
  3. int length; /* property value length */
  4. void *value; /* property value */
  5. struct property *next; /* next property under the same node */
  6. unsigned long _flags;
  7. unsigned int unique_id;
  8. struct bin_attribute attr; /* 属性文件,与sysfs文件系统挂接 */
  9. };

同一个node节点下面的所有属性通过property.next指针进行链接,形成一个单链表。

  1. struct device_node {
  2. const char *name; /* node的名称,取最后一次“/”和“@”之间子串 */
  3. const char *type; /* device_type的属性名称,没有为<NULL> */
  4. phandle phandle; /* phandle属性值 */
  5. const char *full_name; /* 指向该结构体结束的位置,存放node的路径全名,例如:/chosen */
  6. struct fwnode_handle fwnode;
  7. struct property *properties; /* 指向该节点下的第一个属性,其他属性与该属性链表相接 */
  8. struct property *deadprops; /* removed properties */
  9. struct device_node *parent; /* 父节点 */
  10. struct device_node *child; /* 子节点 */
  11. struct device_node *sibling; /* 姊妹节点,与自己同等级的node */
  12. struct kobject kobj; /* sysfs文件系统目录体现 */
  13. unsigned long _flags; /* 当前node状态标志位,见/include/linux/of.h line124-127 */
  14. void *data;
  15. };

设备树中的每一个node节点经过内核kernel处理都会生成一个struct device_node的结构体,struct device_node最终一般会被挂接到具体的struct device结构体。

关于linux解析dtb的详细过程,建议参考文章

http://www.wowotech.net/device_model/dt-code-file-struct-parse.html。

这里以arm、内核版本linux-4.15.9为例,简单分析一下如何解析dts。

Uboot阶段,会将dtb的地址放入r2地寄存器,传给linux内核kernel。

看看解析部分代码,函数调用流程:

__init setup_arch(char **cmdline_p)

|

setup_machine_fdt(__atags_pointer);//获取设备描述符,其中启动信息__atags_pointer是在文件arch/arm/kernel/head-common.S:139中赋值的。

|

early_init_dt_verify(phys_to_virt(dt_phys))//检查dtb的文件结构

|

of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);//从根节点获取最匹配描述符

|

early_init_dt_scan_nodes();//前期扫描设备树,获取启动参数、根节点长度描述信息、解析内存描述节点

|

unflatten_device_tree();//开始把整个设备树完整的解析出来

|

__unflatten_device_tree(initial_boot_params,NULL,&of_root,early_init_dt_alloc_memory_arch, false);//这里全局静态指针变量of_root会指向最终解析出来的设备树

|

unflatten_dt_nodes(blob, NULL, dad, NULL);//第一次扫描,得到设备树的大小

|

early_init_dt_alloc_memory(size + 4, __alignof__(struct device_node));//申请内存存放设备树

|

unflatten_dt_nodes(blob, mem, dad, mynodes);//第二次扫描,解析出来放到上面申请的内存中,同时Mynodes指向该内存,即of_root

|

of_alias_scan(early_init_dt_alloc_memory_arch);//解析chosen和aliases,静态指针变量of_aliases、of_chosen会指向解析出来的chosen、aliases。

|

arm_dt_init_cpu_maps();//解析cpu节点信息,更新cpu掩码等

|

有关设备树的解析部分就结束了。

实例分析

随机选取设备树文件arch/arm/boot/dts/imx6q-gk802.dts

  1. GPIO实例分析

设备树节点信息描述如下

gpio-keys {

compatible = "gpio-keys";

recovery-button {

label = "recovery";

gpios = <&gpio3 16 1>;

linux,code = <0x198>; /* KEY_RESTART */

wakeup-source;

};

};

对驱动文件gpio_keys.c进行分析,

gpio_keys_probe(struct platform_device *pdev);//是驱动的初始化函数,只有当驱动和设备匹配上了,才会触发调用此函数

怎么匹配呢?根据device_id进行匹配

static const struct of_device_id gpio_keys_of_match[] = {

{ .compatible = "gpio-keys", },

{ },

};

可以看到gpio_keys_of_match[0].compatible = "gpio-keys"刚好和设备树的节点gpio-keys的compatible属性匹配compatible = "gpio-keys";

具体匹配过程分析:

static int __init gpio_keys_init(void)

{

return platform_driver_register(&gpio_keys_device_driver);

}

platform_driver_register注册驱动的时候,会查看驱动是否与总线platform上的设备匹配,如果匹配就会回调probe函数,完成驱动的初始化动作。(这里总线platform上有一个device node结构体指针,指向设备树该过程后续分析)。

结论

如果想要驱动和设备树匹配,需要做以下步骤:

  • 定义一个of_device_id静态数组,例如

static const struct of_device_id gpio_keys_of_match[] = {

{ .compatible = "gpio-keys", },

{ },

};

Compatible的值需要和设备树相同。

  • 定义一个platform_driver静态数组,例如

static struct platform_driver gpio_keys_device_driver = {

.probe = gpio_keys_probe,

.driver = {

.name = "gpio-keys",

.pm = &gpio_keys_pm_ops,

.of_match_table = gpio_keys_of_match,

}

};

最后调用platform_driver_register(&gpio_keys_device_driver)完成注册。

(0)

相关推荐

  • 干货|手把手教你写单片机的结构体

    摘要:听说还有好多学单片机的小伙伴不会用结构体?指针和结构体是学单片机必须要掌握的,如果你C语言掌握的不牢,单片机根本学不到精髓,只能完成一些低级的项目.看得懂结构体并且能够灵活运用结构体才能说你入门 ...

  • 马蜂窝网站简单分析

    马蜂窝网站简单分析

  • 3月29日铁路建设政策调整的简单分析

    京广高铁上的动车组 一.新政内容 3月29日,国家发展改革委等单位联合发布<关于进一步做好铁路规划建设工作意见的通知>,限于篇幅不贴原文了,总结如下: 1.严格控制建设既有高铁的平行线路, ...

  • 第一服务基本面简单分析——闲不住的小摸即可

    并把小熊猫[设为星标 简单讨论一下 体量等各方面跟弘阳服务类似,可以不申购,闲不住的小摸即可. 大股东是当代置业,市值只有32亿港币,实力弱小:第一服务的规模也比较弱小(中指院排31),在管面积只有1 ...

  • 五羊本田睿御无声启动原理与启动控制电路简单分析

    作者:木石雅玩 五羊本田睿御WH110T-6电喷摩托车有一个显著特色,就是配备了无声启动和怠速智能启停系统. 这款车的起动机构,由一个三相交流发电机和三相无刷起动电机集合在一起,也就是说它既是发电机, ...

  • 为什么会打算建仓健帆生物?简单分析下健帆的优势和确定性

    通过导入各家上市公司历年财务数据进行测试,在调整模型精度的同时,也在不断发现好公司.(模型选取近5年财务数据,分析了40项财务指标,内核通过AHP层次分析对各指标进行分层复权,再进行评分) 通过对海天 ...

  • 大众汽车,它的优缺点是什么呢,我做了简单分析

    大众汽车在华确实是非常的热销,不过对于大众汽车来说,他的优缺点还是较为明显的,下面我们就来说说. 优点: 1.悠久的历史,进华时间早 1984年德国大众和上海汽车集团合作,成立上海大众汽车有限公司,随 ...

  • 总资产报酬率的计算公式与简单分析

    总资产报酬率的计算公式与简单分析 总资产报酬率,又又称总资产利润率.总资产回报率.资产总额利润率.是指企业息税前利润与平均总资产之间的比率.用以评价企业运用全部资产的总体获利能力,是评价企业资产运营效 ...

  • 肩胛骨内侧缘痛的简单分析

    肩胛骨位置:上角与第2肋骨平齐,下角与第7肋骨平齐 肩胛骨内侧缘痛,即肩胛骨内侧缘与第2~7肋骨区域的疼痛,这个区域所包含的肌肉主要有:菱形肌.上后锯肌.多裂肌.髂肋肌等.在此区域,最常见的损伤肌肉主 ...

  • 2021年核定征收政策会持续到2022年吗,简单分析一下

    核定征收的根本性含义,是采用合理方法核定纳税人应纳税额.既然核定征收是采用了合理方法的措施之一,为什么企业所得税核定征收核准办理的大门将要被缓缓关闭?可以预计的将来,众多"营改增" ...