Linux驱动——笔记

目录

前言:

1.单片机和Linux驱动之间的区别

2.APP如何找驱动 

3.数据传输 

1.APP和驱动间的数据传输

2.驱动和硬件间数据传输

4.如何写驱动程序

1.确定主设备号

2.构造结构体

3.注册

4.入口

5.入口详解

6.设备树

1.内核对设备树的处理

2.设备树语法 

3.节点的常见属性


前言:

        写本篇文章就是记录一下初学Linux驱动对于驱动的疑问,为什么写好了一个驱动程序,然后在应用中调用,例如open、read、write等函数,他会直接去访问你写好的驱动里的函数。

1.单片机和Linux驱动之间的区别

  • 在单片机中可以直接读写寄存器
  • 在linux中无法直接读取寄存器,只能通过驱动进行硬件操作。
  • 单片机:MCU微控制器

    跑Linux的:MPU微处理器,有MMU内存管理单元(有权限管理)

    单片机程序中没有严格的区分APP和Driver,可以直接调用驱动,通过分层思想进行人为区分,Linux中有严格的划分,APP不能直接调用驱动函数。

2.APP如何找驱动 

系统内部,会将open函数中的参数保存在寄存器里 ,如文件地址保存在a寄存器,操作指令保存在R寄存器,然后会调用一条swi汇编指令,这个swi汇编指令最终会导致某个硬件出现异常,一但发送异常就是进入到异常处理函数(由内核提供),在异常处理函数中根据R寄存器中的值,判断你是要调用内核提供的什么函数,如sys_open。

通过swi这种汇编指令触发一个异常,cpu执行异常处理函数(内核提供),在执行之前,设置一个寄存器,用于区分open、read、write

swi是软件异常,software interrupt

  1. open、write、read是一个库写的,如glibc
  2. glibc中通过设置寄存器,并触发swi
  3. 然后cpu处理异常,分辨寄存器中的内容
  4. 判断内容是普通文件还是特殊文件,如果是设备,则调用驱动

为什么打开某个设备文件,能够被正确识别:

根据主设备号找到驱动程序。

以上述的代码片段为例,为什么要去打开“/dev/hello”,我们可以先去查看一下/dev目录下有什么。 

 在开发板上执行:

[root@100ask:/]# cd /dev/
[root@100ask:/dev]# ls -l

可以查看到如下:

 前面带有c开头的都是字符设备节点,4则是他们的主设备号(不是所有的字符设备节点主设备号都是4,这只是我截图恰巧),4旁边就是他们的次设备号。(这是大概的说法,便于理解)

驱动程序是存储在一个结构体链表中,当你使用open去打开某个字符设备节点时 ,他会根据主设备号去到链表中查找驱动程序。

3.数据传输 

1.APP和驱动间的数据传输

  • APP无法直接访问内核:否则内核态容易被破坏了。
  • APP也就无法传递数据给驱动,驱动属于内核的一部分。
  • 驱动也无法直接方位APP的变量。
  • 驱动要访问APP的数据,必须使用这2个函数:
    • copy_to_user

    • copy_from_user

2.驱动和硬件间数据传输

4.如何写驱动程序

这篇主要讲app访问驱动的原理,具体怎么写一个驱动程序可以查看这篇文章:

如何写第一个驱动

1.确定主设备号

既然是根据主设备号查找驱动程序,当然得先分配主设备号。可以通过/proc/devices来找到目前被占用的,也可以直接设置为0,让系统自行选择没有占用的。

2.构造结构体

驱动程序是存储在结构体链表中的,当然得为他构造一个结构体(struct file_operations),才能将他放入链表中。

3.注册

注册就是将这个结构体放入链表中,然后会给你返回一个主设备号。那么以后当我们要访问这个驱动程序的时候,只需要根据主设备号进入链表中查询就可以了。

4.入口

注册函数当然得有人来调用了,这个人就是入口函数,当我们装载一个驱动时,首先会进入驱动程序的入口函数,这些注册函数都是写在入口函数中,将我们构造的结构体放入链表。 

5.入口详解

我们以有多个led为例:

        当你使用register_chrdev注册字符设备驱动后得到一个主设备号,这个主设备号用于标识设备的类型,通常代表一类设备。

        对于有多个 LED 灯的情况,你可以使用class_create创建一个设备类,这样有助于对这一类 LED 设备进行统一的管理和分类展示。设备类主要是在逻辑上对具有相似特性的设备进行分组,它在 /sys/class/下创建一个目录,方便用户和系统以一种更有条理的方式查看和管理设备。

        然后使用device_create为每个具体的 LED 灯创建设备实例,并在 /dev/下创建对应的设备文件节点。通过这种方式,每个 LED 灯都有一个对应的设备文件,可以被用户空间程序通过操作这些设备文件来控制相应的 LED 灯。每个设备实例可以有不同的次设备号,用于区分不同的 LED 灯。

就像是他们有着同一个主设备号,代表他们的逻辑相似,但他们有着不同的次设备号,代表他们是不同的个体,然后 device_create会为他们创建具体的设备节点。

6.设备树

1.内核对设备树的处理

dtb中每一个节点都被转换为device_node结构体

2.设备树语法 

设备树(Device Tree)是一种描述硬件设备的数据结构,用于向操作系统传递硬件信息。以下是设备树的基本语法:

  1. 节点(Nodes):

    • 设备树由一个或多个节点组成,每个节点代表一个硬件设备或硬件组件。
    • 节点用大括号括起来,节点名称放在括号前面,例如:node-name {... }
    • 节点名称通常反映了硬件设备的类型或功能。
  2. 属性(Properties):

    • 节点可以包含一个或多个属性,用于描述硬件设备的特性。
    • 属性用键值对的形式表示,例如:property-name = "value";
    • 属性值可以是字符串、整数、数组等数据类型。
  3. 引用(References):

    • 节点可以通过引用其他节点来表示硬件设备之间的关系。
    • 引用使用节点路径的形式,例如:&node-name
    • 引用可以用于指定设备的父节点、子节点、连接的设备等。
  4. 子节点(Child Nodes):

    • 节点可以包含子节点,用于表示硬件设备的层次结构。
    • 子节点放在父节点的大括号内,例如:parent-node { child-node {... } }
  5. 标签(Labels):

    • 节点可以有一个或多个标签,用于方便地引用节点。
    • 标签用尖括号括起来,放在节点名称后面,例如:node-name<label1><label2> {... }

以下是一个简单的设备树示例:

/dts-v1/;

/ {
    model = "My Board";
    compatible = "my-board,generic-arm";

    cpus {
        #address-cells = <1>;
        #size-cells = <0>;

        cpu@0 {
            compatible = "arm,cortex-a7";
            reg = <0>;
        }
    }

    memory {
        device_type = "memory";
        reg = <0x0 0x20000000 0x0 0x40000000>;
    }
};

在这个示例中,根节点 / 代表整个设备,包含了 model 和 compatible 属性,以及 cpus 和 memory 子节点。cpus 节点包含了一个 cpu@0 子节点,代表一个 CPU。memory 节点描述了设备的内存。

3.节点的常见属性

在设备树中,属性用于描述硬件设备的特性和配置信息。以下是一些常见的设备树属性:

  1. compatible:用于指定设备的兼容性列表。它是一个字符串数组,每个字符串表示一种设备的兼容性。操作系统可以根据这个属性来识别和加载相应的设备驱动程序。

    • 例如:compatible = "fsl,imx6qdl-sabresd", "fsl,imx6q" 表示该设备与飞思卡尔 i.MX6QDL SabreSD 开发板以及其他兼容 i.MX6Q 的设备兼容。
  2. model:描述设备的型号或名称。

    • 例如:model = "My Board Model"
  3. reg:用于描述设备的寄存器地址范围。通常是一个地址和长度的列表。

    • 例如:reg = <0x10000000 0x1000 0x20000000 0x2000> 表示设备的寄存器地址范围从 0x10000000 开始,长度为 0x1000,另一个地址范围从 0x20000000 开始,长度为 0x2000。
  4. interrupts:描述设备的中断请求(IRQ)线。

    • 例如:interrupts = <0 23 4> 表示设备使用中断号 23,触发方式为上升沿触发(4 表示上升沿触发,具体的触发方式定义可能因平台而异)。
  5. clocks:指定设备使用的时钟源。

    • 例如:clocks = <&clocks clk1>, <&clocks clk2> 表示设备使用名为 clk1 和 clk2 的时钟源。
  6. status:表示设备的状态,可以是 "okay"、"disabled" 等。

    • 例如:status = "disabled" 表示设备当前被禁用。
  7. address-cells 和 size-cells:用于描述子节点的地址和大小的单元数量。在设备树的层次结构中,父节点可以包含多个子节点,每个子节点可能有不同的地址和大小。这两个属性告诉操作系统如何解析子节点的地址和大小信息。

    • 例如:在一个总线节点中,address-cells = <2> 和 size-cells = <1> 表示子节点的地址使用两个单元(通常是 32 位地址和 32 位地址偏移),大小使用一个单元(通常是 32 位长度)。
  8. #address-cells 和 #size-cells:与 address-cells 和 size-cells 类似,但用于描述当前节点的地址和大小的单元数量。

    • 例如:在一个设备节点中,#address-cells = <1> 和 #size-cells = <0> 表示该设备的地址使用一个单元(通常是 32 位地址),大小不使用单元(即固定大小)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值