Linux-----驱动

发布于:2025-04-14 ⋅ 阅读:(24) ⋅ 点赞:(0)

一、内核驱动与启动流程

1. Linux内核驱动
  • Nor Flash: 可线性访问,有专门的数据及地址总线(与内存访问方式相同)。

  • Nand Flash: 不可线性访问,访问需要控制逻辑(软件)。

2. Linux启动流程
  • ARM架构:

    • IRAM (4KB): 内部RAM,用于存储初始引导程序。

    • Nor Flash (2M): 存储u-boot程序。

    • 内存 (64M): 用于加载内核和根文件系统。

    • Nand Flash (256M): 存储内核、根文件系统等数据。

  • 启动过程:

    1. Bootloader (u-boot):

      • 初始化CPU、异常向量表、栈、时钟、内存等。

      • 关闭看门狗、中断、Cache、MMU。

      • 初始化相关硬件和软件协议。

      • 将内核加载到内存。

      • 向内核传递参数(根文件系统类型、位置、控制台等)。

      • 启动内核。

    2. 内核 (kernel):

      • 文件管理、内存管理、进程管理、网络管理、设备管理。

      • 启动到最后阶段加载根文件系统。

      • init进程启动后台服务程序、加载配置、启动shell和应用程序。

    3. 根文件系统 (rootfs):

      • 包含程序(应用、系统、命令)、配置文件、库文件、普通文件(txt、mp3)。

3. Windows与Linux对比
  • Windows: 使用BIOS启动。

  • Linux: 使用bootloader引导内核启动,内核加载rootfs。

4. 具体启动步骤
  • Nor Flash:

    • 系统上电后,PC指向0地址,直接执行Nor Flash中的u-boot程序。

  • Nand Flash:

    • 系统上电后,自动搬移u-boot前4KB程序到IRAM。

    • CPU执行IRAM中的代码,u-boot初始化内存并将剩余代码搬移到内存执行。

5. 内核与文件系统
  • 内核 (uImage):

    • 启动前u-boot向内核传递参数(tag_list)。

    • Nand Flash: u-boot直接读取Nand Flash中的uImage并写入内存的0x30008000地址处,启动内核。

    • Ubuntu: 通过TFTP下载uImage到内存的0x30008000地址处,启动内核。

  • 根文件系统 (rootfs):

    • Nand Flash: uImage启动到最后阶段时,直接挂载Nand Flash中的rootfs。

    • Ubuntu: uImage启动到最后阶段时,通过NFS挂载Ubuntu中的rootfs。

6. 前置步骤

  1. 向Nor Flash 0地址处烧写u-boot.bin。

  2. 拷贝uImage到Ubuntu的TFTP服务目录下。

  3. 将rootfs.tar.gz拷贝到Ubuntu的NFS服务目录下,并解压

    sudo tar -xvf rootfs.tar.gz
7. U-Boot命令
  • 环境变量管理:

    • printenv: 打印环境变量。

    • reset: 重启。

    • setenv serverip 192.168.1.3: 设置环境变量。

    • saveenv: 保存环境变量到Nand Flash。

    • setenv serverip: 删除环境变量。

  • 下载与启动:

    • tftp 0x30008000 uImage: 通过TFTP协议下载uImage到内存的0x30008000地址处。

    • bootm 0x30008000: 启动内存0x30008000地址处的内核。

    • go 0x30008000: 运行内存0x30008000地址处的程序。

8. 设置启动参数
setenv bootargs console=ttySAC0,115200 root=/dev/nfs nfsroot=192.168.1.3:/home/linux/nfs/rootfs ip=192.168.1.123 init=/linuxrc
  • console: 控制台(终端)。

  • root: 根文件系统类型。

  • nfsroot: 根文件系统位置。

  • ip: 内核阶段使用的IP。

  • init: 指定init进程。

二、内核编译

1. 内核编译步骤
  • Kconfig: 定义make menuconfig的配置选项。

  • make menuconfig: 内核配置。

  • .config: 配置文件,决定哪些文件被编译进内核。

    • CONFIG_SSL = n: 不启用SSL。

    • CONFIG_MM = y: 启用内存管理。

  • makefile: 条件编译,编译内核。

2. 内核镜像类型
  • Image: 可以直接使用的内核镜像。

  • zImage: 一段解压代码 + Image的压缩文件。

  • uImage: 64字节的头信息 + zImage。

3. 地址相关代码
  • 地址相关代码: 链接地址和加载地址一致。

  • 地址无关代码: 链接地址和加载地址无关。

4. 跳转指令
  • 相对跳转、短跳转: b fun

  • 绝对跳转、长跳转: ldr pc, 0x00000000

5. 内核目录结构

6. 向内核新增文件

以向drivers/char下添加demo.c为例:

  1. drivers/char目录下新建并编辑demo.c

  2. 修改同层目录下的Makefile,添加:

    makefile
    obj-$(CONFIG_DEMO) += demo.o
  3. 修改同层目录下的Kconfig,添加一个DEMO的配置。

  4. 执行make menuconfig

  5. 执行make uImage

7. 内核编译命令
  • 配置内核

    cp config_mini2440_t35 .config
    make menuconfig
  • 编译内核

    make uImage
8. 内核镜像说明
  • Image: 可直接使用的内核镜像。

  • zImage: 压缩的内核镜像,包含解压代码。

  • uImage: 带有64字节头信息的压缩内核镜像。

9. Makefile和Kconfig
  • 每层目录都有MakefileKconfig文件,用于配置和编译内核。

10. 编译流程总结
  1. 配置内核: 使用make menuconfig选择内核配置选项。

  2. 编译内核: 使用make uImage生成内核镜像。

  3. 验证内核: 确保生成的内核镜像可以正常启动。

11. 注意事项
  • 配置文件: .config文件决定了哪些模块被编译进内核。

  • 条件编译: 使用obj-$(CONFIG_XXX)进行条件编译。

  • 目录结构: 每层目录都有MakefileKconfig文件,确保编译过程正确。

三、驱动程序

1. 设备文件与驱动模块
  • 设备文件: 用户空间程序通过设备文件与驱动程序交互。

    • 示例: open("/dev/led");

  • 驱动模块: 内核中的驱动程序负责控制硬件设备。

    • 示例: sys_open(led)调用驱动模块。

2. 设备驱动类型
  • 字符设备驱动: 数据按顺序访问,90%以上的设备使用字符设备驱动。

  • 块设备驱动: 可以随机访问,主要用于存储设备。

  • 网络设备驱动: 网卡,集成复杂协议,通过套接字通信,没有设备号,靠名字维护。

3. 设备号
  • 设备号: 用于标识设备。

    • 主设备号 (高12位): 区分设备类型。

    • 次设备号 (低20位): 区分同类的不同设备。

  • 示例: dev_t 是32位设备号。

4. 创建设备节点
  • 使用mknod命令创建设备节点

    mknod /dev/demo3 c 255 0
    • /dev/demo3: 设备节点名。

    • c: 字符设备。

    • 255: 主设备号。

    • 0: 次设备号。

5. 驱动模块结构
  • 驱动模块: 包含openreadwriteioctlclose等函数。

  • 示例: drv_leddrv_keydrv_adc分别对应LED、按键、ADC设备。

6. 设备驱动流程
  1. 应用程序调用设备文件:

    • open("/dev/led");

  2. 内核调用驱动模块:

    • sys_open(led)drv_led

  3. 驱动模块控制硬件设备:

    • drv_led控制LED设备。

7. 设备号与驱动模块关系
  • 每个设备号对应一个驱动模块。

  • 内核通过设备号找到对应的驱动模块。

8. 设备号结构
  • 设备类型 (魔幻数): 8位,标识设备类型。

  • 命令编号: 8位,标识具体命令。

  • 数据流向: 2位,标识数据方向。

  • 参数大小: 14位,标识参数大小。

9. 设备驱动总结
  • 字符设备: 顺序访问,适用于大多数设备。

  • 块设备: 随机访问,适用于存储设备。

  • 网络设备: 复杂协议,通过套接字通信。

  • 设备号: 区分设备类型和具体设备。

  • 驱动模块: 内核中的程序,负责控制硬件设备。

10. 关键命令
  • 创建设备节点: mknod

  • 设备文件操作: openreadwriteioctlclose

11. 注意事项
  • 设备号分配: 确保主设备号和次设备号唯一。

  • 驱动模块编写: 需要实现openreadwrite等函数。

  • 设备文件操作: 应用程序通过设备文件与驱动模块交互。