Linux第73步_学习Linux设备树和“OF函数”

这篇具有很好参考价值的文章主要介绍了Linux第73步_学习Linux设备树和“OF函数”。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

掌握设备树是 Linux驱动开发人员必备的技能

1、了解设备树文件

在3.x版本以前的Linux内核源码中,存在大量的“arc/arm/mach-xxx”和“arc/arm/plat-xxx”文件夹,里面很多个“.c”和“.h”文件,它们用来描述设备信息。而现在的ARM架构是采用“设备树”来描述设备信息。“设备树”英文名字叫“Device Tree”。描述设备树的文件叫做“DTS”,是Device Tree Source的缩写。它主要用来描述板子上有哪些设备构成,如:I2C控制器、GPIO控制器、SPI控制器、UART控制器等。

设备树源文件扩展名为“.dts”,设备树头文件扩展名为“.dtsi”,它们经过DTC工具编译后生成的二进制文件,就是“.dtb”文件。

“.dts”设备树文件可以通过“#include”来引用“.h”、“.dtsi”和“.dts”文件。在编写设备树头文件时,我们最好选择“.dtsi”作为后缀。

一般“.dtsi”文件用于描述芯片的“内部外设信息”,比如CPU架构、主频、外设寄存器地址范围,如:UART、IIC、SPI、TIMER等等。

2、了解节点

设备树是描述板子上的“设备信息”的文件,均采用“树形结构”。每个设备树文件只有一个“根节点”,而“不同设备树文件的根节点”会合在一起形成一个根节点。每个设备都是一个节点,称之为“设备节点”,每个节点都采用“属性信息”来描述其“节点信息”。

认识“标准属性”、“节点”和“节点标签”,见下图:

Linux第73步_学习Linux设备树和“OF函数”,产品研发,linux,学习,STM32MP157,经验分享

标准属性

1)、compatiable属性,也叫兼容性属性。

如:compatible = "cirrus,cs42l51";

这个compatible只有一个属性值,就是“cirrus,cs42l51”,其中“cirrus”表示厂商,“cs42l51”表示驱动模块。

再如:compatible = "cirrus,cs42l51","cirrus,My_cs42l51";

这个compatible就有两个属性值,就是“cirrus,cs42l51”和"cirrus,My_cs42l51",其中“cirrus”表示厂商,“cs42l51”表示驱动模块,“My_cs42l51”表示驱动模块。

驱动文件的“OF匹配表”

const struct of_device_id cs42l51_of_match[] = { { .compatible = "cirrus,cs42l51", },{ }};

设备首先会使用第一个兼容值在Limux内核里面查找。看看能不能找到与之匹配的驱动文件,如果没有找到,就使用第二个兼容值查,以此类推,直到查找完 commpatible属性中的所有值。

compatible属性用于将设备和驱动绑定起来。驱动程序文件有一个“OF匹配表”,这个“OF匹配表”保存着一些compatible的值,如果设备节点的compatible属性值和“OF匹配表”中的任何一个值相等,那么这个设备就可以使用这个驱动。

2)、model属性

用于描述开发板的名字或设备模块的信息。

比如:model = "STMicroelectronics STM32MP157C-DK2 Discovery Board";

3)、status属性

status属性值

描述

okay

表明设备是可操作的

disabled

表明设备当前是不可操作的,但是在未来可以变为可操作的,比如:热插拔设备插入以后。至于disabled的具体含义还要看设备的绑定文档。

fail

表明设备不可操作,设备检测到了一系列的错误,而且设备也不大可能变得可操作。

fail-sss

含义和“fai1”相同,后面的“sss部分”是检测到的错误内容。

Linux第73步_学习Linux设备树和“OF函数”,产品研发,linux,学习,STM32MP157,经验分享

4)、reg属性

reg属性的值一般是(address,length)对。

reg 属性一般用于描述“设备地址空间资源信息”或者“设备地址信息”,比如某个外设的“寄存器地址范围信息”,或者I2C器件的设备地址等。

5)、ranges属性

ranges = <child-bus-address,parent-bus-address,length>;

child-bus-address表示“子总线地址空间的物理地址”;

parent-bus-address表示“父总线地址空间的物理地址”;

length表示“子总线地址空间的长度”;

若ranges属性值为空,则说明子地址空间和父地址空间完全相同;

6)、“#address-cells”和“#size-cells”属性

#address-cells的属性值为无符号32位整型,用于描述子节点的“reg和ranges”中的address所占的位数,1表示32位,2表示64位;

#size-cells的属性值为无符号32位整型,用于描述子节点的“reg和ranges”中的length所占的位数,0表示没有位数,1表示32位,2表示64位;

举例说明“reg、 #address-cells和#size-cells”属性:

cpus {   #address-cells = <1>;//表示子节点cpureg和rangesaddress占32个位

   #size-cells = <0>;   //表示子节点cpureg和rangeslength占0个位   cpu0: cpu@0 {      compatible = "arm,cortex-a7";      device_type = "cpu";      reg = <0>;        //表示addres=0, 没有length,和前面定义一致

      clocks = <&scmi0_clk CK_SCMI0_MPU>;      clock-names = "cpu";      operating-points-v2 = <&cpu0_opp_table>;      nvmem-cells = <&part_number_otp>;       nvmem-cell-names = "part_number";       #cooling-cells = <2>;    }; };

scmi_sram: sram@2ffff000 {   compatible = "mmio-sram";   reg = <0x2ffff000 0x1000>//表示address=0x2ffff000,length=0x1000

   #address-cells = <1>;//表示scmi_shmreg和rangesaddress占32个位

   #size-cells = <1>;   //表示scmi_shmreg和rangeslength占32个位

   ranges = <0 0x2ffff000 0x1000>;  //分别为32位,和前面定义一致

   scmi0_shm: scmi_shm@0 {      reg = <0 0x80>;    //reg的address和length分别为32位,和前面定义一致

   };

};

soc {

   compatible = "simple-bus";   #address-cells = <1>; //表示sramreg和rangesaddres长度为32个位   #size-cells = <1>;    //表示sramreg和rangeslength长度为32个位

   interrupt-parent = <&intc>;   ranges = <0 0x10000000 0x100000>;

   sram: sram@10000000 {      compatible = "mmio-sram";      reg = <0x0 0x60000>;//reg的address和length均为32位,和前面定义一致

      #address-cells = <1>;      #size-cells = <1>;      ranges = <0 0x10000000 0x60000>;  //均为32位,和前面定义一致

   };};

以STM32MP157为例,创建设备树文件myfrst.dts ,要求在设备树里面的内容如下:

1)、芯片是由两个Cortex-A7 架构的32位CPU和Cortex-M4组成。

2)、STM32MP157内部sram,起始地址为 0x10000000,大小为384KB(0x60000)。

先搭建 “根节点框架”,如下:

/{   compatible = "st,stm32mp157d-atk", "st,stm32mp157"; };

接着添加cpus节点,如下:

/{   compatible = "st,stm32mp157d-atk", "st,stm32mp157";   cpus{

     #address-cells = <1>;//表示子节点cpureg和rangesaddress占32个位

     #size-cells = <0>;   //表示子节点cpureg和rangeslength占0个位

   };

 };

接着添加cpu0节点,如下:

/{   compatible = "st,stm32mp157d-atk", "st,stm32mp157";   cpus{

     #address-cells = <1>;//表示子节点cpureg和rangesaddress占32个位

     #size-cells = <0>;   //表示子节点cpureg和rangeslength占0个位

      cpu0: cpu@0 {        compatible = "arm,cortex-a7";        device_type = "cpu";        reg = <0>;        //表示addres=0, 没有length,和前面定义一致

     };

   };

 };

接着添加cpu1节点,如下:

/{   compatible = "st,stm32mp157d-atk", "st,stm32mp157";   cpus{

     #address-cells = <1>;//表示子节点cpu的reg和ranges的address占32个位

     #size-cells = <0>;   //表示子节点cpureg和rangeslength占0个位

      cpu0: cpu@0 {        compatible = "arm,cortex-a7";        device_type = "cpu";        reg = <0>;        //表示addres=0, 没有length,和前面定义一致

     };

      cpu1: cpu@1 {        compatible = "arm,cortex-m4";        device_type = "cpu";        reg = <1>;        //表示addres=1, 没有length,和前面定义一致

     };

   };

 };

接着添加soc节点,如下:

/{   compatible = "st,stm32mp157d-atk", "st,stm32mp157";   cpus{

     #address-cells = <1>;//表示子节点cpureg和rangesaddress占32个位

     #size-cells = <0>;   //表示子节点cpureg和rangeslength占0个位

      cpu0: cpu@0 {        compatible = "arm,cortex-a7";        device_type = "cpu";        reg = <0>;        //表示addres=0, 没有length,和前面定义一致

     };

      cpu1: cpu@1 {        compatible = "arm,cortex-m4";        device_type = "cpu";        reg = <1>;        //表示addres=1, 没有length,和前面定义一致

     };

   };

   soc {

      compatible = "simple-bus";      #address-cells = <1>; //表示sramreg和rangesaddres长度为32个位      #size-cells = <1>;    //表示sramreg和rangeslength长度为32个位

      ranges; //说明子地址空间和父地址空间完全相同;

   };

 };

接着添加sram节点,如下:

/{   compatible = "st,stm32mp157d-atk", "st,stm32mp157";   cpus{

     #address-cells = <1>;//表示子节点cpureg和rangesaddress占32个位

     #size-cells = <0>;   //表示子节点cpureg和rangeslength占0个位

      cpu0: cpu@0 {        compatible = "arm,cortex-a7";        device_type = "cpu";        reg = <0>;        //表示addres=0, 没有length,和前面定义一致

     };

      cpu1: cpu@1 {        compatible = "arm,cortex-m4";        device_type = "cpu";        reg = <1>;        //表示addres=1, 没有length,和前面定义一致

     };

   };

   soc {

      compatible = "simple-bus";      #address-cells = <1>; //表示sramreg和rangesaddres长度为32个位      #size-cells = <1>;    //表示sramreg和rangeslength长度为32个位

      ranges; //说明子地址空间和父地址空间完全相同;

      

      sram: sram@10000000 {        compatible = "mmio-sram";        reg = <0x0 0x60000>;//address和length均为32位,和前面定义一致

        ranges = <0 0x10000000 0x60000>;  //均为32位,和前面定义一致

     };

   };

 };

3、了解特殊节点

1)、aliases子节点

aliases {

   serial0 = &uart4;  //给&uart4起个别名叫“serial0”

};

2)、chosen子节点

chosen不是一个真实的设备,chosen节点主要是为了uboot向 Linux内核传递数据。

chosen {

   stdout-path = "serial0:115200n8";

//设置“stdout-path”属性,表示标准输出使用“serial0

};

4、学习“OF函数”

“OF函数原型”都定义在“include/linux/of.h”文件中。

1)、了解相关结构体

Linux内核使用device_node结构体来描述一个节点。

struct device_node {

const char *name;  /*节点名字*/

phandle phandle;

const char *full_name;  /*节点全名*/

struct fwnode_handle fwnode;

struct property *properties; /*属性*/

struct property *deadprops; /* removed properties */

struct device_node *parent; /*父节点*/

struct device_node *child;  /*子节点*/

struct device_node *sibling;

#if defined(CONFIG_OF_KOBJ)

struct kobject kobj;

#endif

unsigned long _flags;

void *data;

#if defined(CONFIG_SPARC)

unsigned int unique_id;

struct of_irq_controller *irq_trans;

#endif

};

Linux内核中使用结构体property表示属性

struct property {

char *name;           /*属性名字*/

int length;              /*属性长度*/

void *value;          /*属性值*/

struct property *next;  /*下一个属性*/

#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)

unsigned long _flags;

#endif

#if defined(CONFIG_OF_PROMTREE)

unsigned int unique_id;

#endif

#if defined(CONFIG_OF_KOBJ)

struct bin_attribute attr;

#endif

}

resource结构体定义在文件 include/linux/ioport.h中。

struct resource {

resource_size_t start;   /*开始地址,占32位*/

resource_size_t end;     /*结束地址,占32位*/

const char *name;        /*资源的名字*/

unsigned long flags;     /*资源标志或资源类型*/

unsigned long desc;

struct resource *parent, *sibling, *child;

};

2)、通过节点的名字查找指定的节点:

struct device_node *of_find_node_by_name(struct device_node *from, const char *name)

from:开始查找的节点,如果为NULL,表示从根节点开始查找整个设备树。
name:要查找的节点名字。
返回值:返回找到的节点,如果为NULL,表示查找失败。

3)、通过device_type属性查找指定的节点

struct device_node *of_find_node_by_type(struct device_node *from, const char *type)

from:开始查找的节点,如果为NULL,表示从根节点开始查找整个设备树;

type:要查找的节点对应的type字符串,也就是 device_type属性值。

返回值:返回找到的节点,如果为NULL,表示查找失败。

4)、根据device_type属性和compatible属性查找指定的节点

struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compatible)

from:开始查找的节点,如果为NULL,表示从根节点开始查找整个设备树;

type:要查找的节点对应的type字符串,也就是device_type属性值。若为NULL,则可忽略掉device_type属性值;

compatible:要查找的节点所对应的compatible 属性列表;

返回值:返回找到的节点,如果为NULL,表示查找失败。

5)、通过of_device_id匹配表来查找指定的节点

struct device_node *of_find_matching_node_and_match(struct device_node *from, const struct of_device_id *matches, const struct of_device_id **match)

from:开始查找的节点,如果为NULL,表示从根节点开始查找整个设备树。matches: of_device_id匹配表,也就是在此匹配表里面查找节点。

match:找到的匹配的of_device_id

返回值:返回找到的节点,如果为NULL,表示查找失败。

6)、通过路径来查找指定的节点

inline struct device_node *of_find_node_by_path(const char *path)

path:带有全路径的节点名,可以使用节点的别名,比如“/backlight”就是 backlight 这个节点的全路径。

返回值:返回找到的节点,如果为NULL,表示查找失败。

7)、获取指定节点的父节点

struct device_node *of_get_parent(const struct device_node *node)

node:要查找的父节点的节点名称。返回值: 返回找到的父节点。

8)、采用迭代方式查找子节点,即查找“prev”的下一个节点。

struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev)

node:父节点。

prev:前一个子节点,也就是从哪一个子节点开始迭代的查找下一个子节点。若设置为NULL,则表示从第一个子节点开始。

返回值: 返回找到的下一个子节点。

9)、查找指定的属性

property *of_find_property(const struct device_node *np, const char *name, int *lenp)

np:设备节点。

name:属性名字。

lenp:属性值的字节数

返回值:返回找到的属性。

10)、获取属性中元素的数量

int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size)

np:设备节点。

proname:需要统计元素数量的属性名字。

elem_size:元素长度。

返回值: 返回得到的属性元素数量。

11)、从属性中获取指定标号的数据值

int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value)

np:设备节点。

proname:要读取的属性名字。index:要读取的值标号;

out_value:读取到的值返回值:0读取成功,负值,读取失败,-EINVAL表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。

12)、读取属性中u8类型的数组数据

int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz)

np:设备节点。proname:要读取的属性名字。out_value:读取到的数组值,数据类型为u8。

sz:要读取的数组元素数量。返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。

13)、读取属性中u16类型的数组数据

int of_property_read_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz)

np:设备节点。proname:要读取的属性名字。out_value:读取到的数组值,数据类型为u16。sz:要读取的数组元素数量。返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。

14)、读取属性中u32类型的数组数据

int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz)

np:设备节点。proname:要读取的属性名字。out_value:读取到的数组值,数据类型为u32。sz:要读取的数组元素数量。返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。

15)、读取属性中u64类型的数组数据

int of_property_read_u64_array(const struct device_node *np, const char *propname, u64 *out_values, size_t sz)

np:设备节点。proname:要读取的属性名字。out_value:读取到的数组值,数据类型为u64。sz:要读取的数组元素数量。返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。

16)、读取属性中只有一个u8类型的数据

int of_property_read_u8(const struct device_node *np, const char *propname, u8 *out_value)

np:设备节点。proname:要读取的属性名字。out_value:读取到的u8类型的数据值。返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。

17)、读取属性中只有一个u16类型的数据

int of_property_read_u16(const struct device_node *np, const char *propname, u16 *out_value)

np:设备节点。proname:要读取的属性名字。out_value:读取到的u16类型的数据值。返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。

18)、读取属性中只有一个u32类型的数据

int of_property_read_u16(const struct device_node *np, const char *propname, u32 *out_value)

np:设备节点。proname:要读取的属性名字。out_value:读取到的u32类型的数据值。返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。

19)、读取属性中只有一个u64类型的数据

int of_property_read_u16(const struct device_node *np, const char *propname, u64 *out_value)

np:设备节点。proname:要读取的属性名字。out_value:读取到的u64类型的数据值。返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。

20)、读取属性中的字符串值

int of_property_read_string(struct device_node *np, const char *propname, const char **out_string)

np:设备节点。proname:要读取的属性名字。out_string:读取到的字符串值。返回值:0,读取成功,负值,读取失败。

21)、获取“#address-cells”属性值

int of_n_addr_cells(struct device_node *np)

np:设备节点。返回值:返回“#address-cells”的属性值。

22)、获取“#size-cells”属性值

int of_n_size_cells(struct device_node *np)

np:设备节点。返回值:返回“#size-cells”的属性值。

23)、査看“节点的compatible属性”是否有包含“compat指定的字符串”,也就是检查设备节点的兼容性

int of_device_is_compatible(const struct device_node *device, const char *compat)

device:设备节点;

compat:要查看的字符串。返回值:0,表示“节点的compatible属性”中不包含“compat指定的字符串”;正数,表示“节点的compatible属性”中包含“compat指定的字符串”。

24)、读取“reg”或者“assigned-addresses”属性值

const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, unsigned int *flags)

dev:设备节点。

index:要读取的地址标号。

size:地址长度。flags:参数,比如“IORESOURCE_IO”、“IORESOURCE_MEM”等返回值:返回读取到的地址数据首地址,若返回值为NUIL,则表示读取失败。

25)、将从设备树读取到的地址addr转换为物理地址

u64 of_translate_address(struct device_node *dev, const __be32 *addr)

dev:设备节点。

addr:要转换的地址。

返回值:返回得到的物理地址,如果为 OF_BAD_ADDR,则表示转换失败。

26)、将reg属性值转换为resource结构体类型

int of_address_to_resource(struct device_node *dev, int index, struct resource *r)

dev:设备节点。

index:地址资源标号

r:得到的resource 类型的资源值。

返回值:0,成功:负值,失败。

27)、将“reg属性中地址信息”转换为“虚拟地址”,如果reg属性有多段的话,可以通过index参数指定要完成内存映射的是哪一段

void __iomem *of_iomap(struct device_node *np, int index)

np:设备节点。

index:reg属性中要完成内存映射的段,如果reg属性只有一段的话,则index=0。

返回值:经过内存映射后的虚拟内存首地址,如果为NULL的话,则表示内存映射失败。

注意:也可以使用ioremap()函数来完成物理地址到虚拟地址的内存映射。在采用设备树以后,大部分的驱动都使用of_iomap()函数来获取内存地址所对应的虚拟地址。文章来源地址https://www.toymoban.com/news/detail-839039.html

到了这里,关于Linux第73步_学习Linux设备树和“OF函数”的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • LDD学习笔记 -- Linux字符设备驱动

    字符驱动程序用于与Linux内核中的设备进行交互; 字符设备指的是像内存区域这样的硬件组件,通常称为伪设备; 用户空间应用程序通常使用 open read write 等系统调用与这些设备通信; 把用户空间的系统调用连接到设备驱动的系统调用实现方法上。 内核的虚拟文件系统 vir

    2024年02月02日
    浏览(45)
  • shell_73.Linux使用新 shell 启动脚本

    每次启动新 shell,bash shell 都会运行.bashrc 文件。①对此进行验证,可以使用这种方法:在 主目录下的.bashrc 文件中加入一条简单的 echo 语句,然后启动一个新 shell。 .bashrc 文件通常也借由某个 bash 启动文件来运行,因为.bashrc 文件会运行两次:一次是当 用户登录 bash shell 时,

    2024年02月05日
    浏览(57)
  • 【Linux】RK3399平台开发系列——设备树的学习笔记

    设备树(Device Tree)是用于描述硬件设备和系统关系的树形数据结构,主要用于 Linux 操作系统中的设备驱动程序。在嵌入式系统中,由于硬件的多样性和复杂性,设备树变得越来越流行,用户可以用设备树来描述各种外设的属性和配置信息,以帮助内核识别和管理外设。 在

    2024年02月04日
    浏览(45)
  • RK3588平台开发系列讲解(驱动基础篇)设备树常用 of 函数

    平台 内核版本 安卓版本 RK3588 Linux 5.10 Android 12 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 设备树描述了设备的详细信息,这些信息包括数字类型的、字符串类型的、数组类型的,我们在编写驱动的时候需要获取到这些信息。比如设备树使用 reg 属性描述了某个

    2024年02月08日
    浏览(55)
  • PCIE学习系列 五(Linux之PCIe设备驱动开发框架)

    本文讲述一个开源的PCIe设备驱动,通过这个例子可以基本上理解所有的PCIe设备驱动。后续也会做关于Linux各类驱动的文章。 通过前面的学习,我们知道PCIe设备访问之前需要先做枚举。一般来说,PCI设备的枚举操作不需要我们来做,BIOS或者系统初始化时已经做好了,当系统枚

    2024年02月05日
    浏览(58)
  • Linux设备驱动开发学习笔记(等待队列,锁,字符驱动程序,设备树,i2C...)

    container_of函数可以通过结构体的成员变量检索出整个结构体 函数原型: 内核开发者只实现了循环双链表,因为这个结构能够实现FIFO和LIFO,并且内核开发者要保持最少代码。 为了支持链表,代码中要添加的头文件是linux/list.h。内核中链表实现核心部分的数据结构 是struct li

    2024年01月22日
    浏览(55)
  • 【genius_platform软件平台开发】第九十七讲:linux设备驱动中信号(signal函数)的异步通知机制

    意思是: 一旦设备就绪,则主动通知应用程序 ,这样应用程序根本就不需要查询设备状态,这一点非常 类似于硬件上“中断”的概念 ,比较准确的称谓是“ 信号驱动的异步I/O ”。信号是在软件层次上对 中断机制的一种模拟 ,在原理上,一个进程收到一个信号与处理器收到一

    2024年02月08日
    浏览(63)
  • 【嵌入式Linux学习笔记】platform设备驱动和input子系统

    对于Linux这种庞大的操作系统,代码重用性非常重要,所以需要有相关的机制来提升效率,去除重复无意义的代码,尤其是对于驱动程序,所以就有了platform和INPUT子系统这两种工作机制。 学习视频地址:【正点原子】STM32MP157开发板 platform 驱动框架分为总线、设备和驱动。总

    2024年02月07日
    浏览(60)
  • 【IMX6ULL驱动开发学习】12.Linux驱动之设备树

    承接上一篇博客 【IMX6ULL驱动开发学习】11.驱动设计之面向对象_分层思想(学习设备树过渡部分) 代码获取: https://gitee.com/chenshao777/imx6-ull_-drivers 我后面将三个层合并了(实际上只有前两层),合并成一个dev_drv.c了,暂时没有加GPIO操作,只是个框架 合并前的代码在 11.butt

    2024年02月13日
    浏览(47)
  • Linux学习笔记 : ARM64 平台下 qemu virt 有默认的设备树 dtb

    首先想通过 Linux qemu 验证 设备树的加载与设备节点解析,原因是 qemu 可以 软件调试,而普通的 Linux 开发板,Linux 内核驱动调试起来反而很复杂。 【记录】我竟然还没有在Linux 真实开发板上调试过Linux 内核,感觉自己就是个工具,配置、修改、下载,有问题加几行 LOG 日志分

    2024年01月20日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包