Linux驱动编译与加载

一、内核源码树

bash 复制代码
Linux-4.9.88/
├── Makefile          # 顶层 Makefile,内核编译入口
├── Kconfig           # 顶层配置菜单入口
├── .config           # 保存 menuconfig 的选择结果
│
├── arch/             # 和 CPU 架构相关的代码
│   └── arm/          # ARM 架构相关代码
│       ├── configs/  # 默认配置文件
│       └── boot/
│           └── dts/  # 设备树文件
│
├── drivers/          # 各种设备驱动
│   ├── char/         # 字符设备驱动
│   ├── gpio/         # GPIO 驱动
│   ├── i2c/          # I2C 驱动
│   ├── spi/          # SPI 驱动
│   ├── net/          # 网络设备驱动
│   └── leds/         # LED 子系统驱动
│
├── include/          # 内核头文件
│   └── linux/        # 驱动常用头文件
│
├── fs/               # 文件系统
├── kernel/           # 内核核心代码
├── mm/               # 内存管理
├── net/              # 网络协议栈
└── scripts/          # 编译脚本和配置工具

写 LED 字符设备驱动时,需要关注的目录树:

arduino 复制代码
Linux-4.9.88/
├── Makefile
├── .config
├── drivers/
│   └── char/
│       ├── Makefile
│       ├── Kconfig
│       └── led_drivers/
│           ├── led_drv.c
│           ├── Makefile
│           └── Kconfig
├── arch/
│   └── arm/
│       └── boot/
│           └── dts/
└── include/
    └── linux/

二、两种添加驱动的方法

方法一:外部模块编译

  驱动源码放在内核源码目录外面,例如:

arduino 复制代码
内核源码树:/home/book/imx6ull-sdk/Linux-4.9.88
驱动源码:/home/book/driver/led/led_drv.c

我们需要在驱动源码目录下建立外部makefile,但是也需要借用内核源码的makefile进行编译,例如:

javascript 复制代码
KERN_DIR = /home/book/imx6ull-sdk/Linux-4.9.88

obj-m += led_drv.o                      //obj-m:Linux 内核 Kbuild 编译系统中的变量
                                        //m:编译为模块,不编译进内核
                                        //如果不写 obj-m += led_drv.o,Linux 内核编译
                                        //系统会进入你当前目录看 Makefile,但它不知道要
                                        //把哪个 .c 文件编译成 .ko,所以一般不会编译你的 led_drv.c
all:
	make -C $(KERN_DIR) M=$(PWD) modules  //make -C $(KERN_DIR):进入内核源码KERN_DIR目录,使用内核编译系统
                                        //M=$(PWD):告诉内核:外部模块源码在当前目录
                                        //modules:告诉内核:只编译模块,不是整个内核
clean:
	make -C $(KERN_DIR) M=$(PWD) clean

这种方式不会改内核源码树,也不需要 Kconfig。

方法二:加入内核源码树编译

  驱动源码放在内核源码目录下,例如:

arduino 复制代码
Linux-4.9.88/drivers/char/led_drivers/led_drv.c

我们需要修改以下目录,将该驱动类似于其他驱动一样加入到内核源码的目录

scss 复制代码
drivers/char/Makefile
drivers/char/led_drivers/Makefile
drivers/char/Kconfig
drivers/char/led_drivers/Kconfig

源码放进内核源码树
修改 Kconfig 和 Makefile
这里修改drivers/char/Makefile时,通常加上obj-y(或者$(CONFIG_LED_DRV)) += led_drivers/,
它的意思不是"把文件夹编译成 .o",而是:编译当前目录时,进入 led_drivers 子目录继续读取里面的 Makefile

通过 menuconfig 选择 y/m/n
执行 make 或 make modules 自动编译

三、Kconfig、menuconfig、.config、Makefile 的关系

整体关系:

ini 复制代码
Kconfig 文件
  |
  |  定义有哪些配置选项
  |  例如:CONFIG_LED_DRV
  v
make menuconfig
  |
  |  图形/菜单方式让用户选择 y / m / n
  v
.config 文件
  |
  |  保存最终选择结果
  |  例如:
  |  CONFIG_LED_DRV=y
  |  CONFIG_KEY_DRV=m
  v
Makefile
  |
  |  根据 CONFIG_xxx 的值决定编译进内核还是编译成模块
  v
生成内核镜像 zImage / Image 或 .ko 模块

1.Kconfig 是什么?

  Kconfig 是用来定义配置菜单和配置选项的文件。

  比如驱动目录中可能有一个 Kconfig:

arduino 复制代码
config LED_DRV                  //config LED_DRV最终会变为CONFIG_LED_DRV
    tristate "My LED Driver"
    default n
    help
      This is a simple LED driver.

它的意思是:

复制代码
定义了一个配置项:LED_DRV

在 menuconfig 中显示为:
My LED Driver

它可以选择:
y:编译进内核
m:编译成模块 .ko
n:不编译

2.menuconfig 是什么?

  menuconfig 不是一个文件,而是一个配置界面。

  当我们执行make menuconfig后会打开一个菜单界面,可以在里面选择:

css 复制代码
[*] 编译进内核                     //[*] -> y
[M] 编译成模块                     //[M] -> m
[ ] 不编译                         //[ ] -> n

例如:在 menuconfig 中选择了 LED 驱动,然后选择了编译成模块,保存退出后,.config 文件中会出现

ini 复制代码
CONFIG_LED_DRV=m

3..config 是什么?

  .config 是 menuconfig 保存出来的最终配置文件。它记录了用户选择了哪些功能,所以 .config 是内核编译时真正参考的配置结果。 例如:

ini 复制代码
CONFIG_LED_DRV=y
表示 LED 驱动编译进内核。

CONFIG_KEY_DRV=m
表示 KEY 驱动编译成 .ko 模块。

# CONFIG_BEEP_DRV is not set
表示 BEEP 驱动不编译。

4.Makefile 是什么?

  Makefile 决定具体编译哪些源码文件。

  比如某个驱动目录下的 Makefile:

javascript 复制代码
obj-$(CONFIG_LED_DRV) += led_drv.o
obj-$(CONFIG_KEY_DRV) += key_drv.o
obj-$(CONFIG_BEEP_DRV) += beep_drv.o

它会根据 .config 中的配置结果来决定编译方式,假设 .config 中是:

ini 复制代码
CONFIG_LED_DRV=y
CONFIG_KEY_DRV=m
# CONFIG_BEEP_DRV is not set

展开的结果如下:

复制代码
obj-y += led_drv.o
表示led_drv.o 编译进内核

obj-m += key_drv.o
表示key_drv.o 编译成 key_drv.ko 模块

obj-  += beep_drv.o
beep_drv.o 不编译

综上所述:Kconfig / menuconfig / .config 决定"某个功能要不要编、编进内核还是编成模块";Makefile 根据这个结果,决定"具体编译哪些 .c 文件,以及怎么组织生成目标文件"。

相关推荐
用户805533698038 小时前
Input 子系统架构:Core、Handler、Driver 三层是怎么协作的
linux·嵌入式
用户805533698038 小时前
RK-Forge外设系列开篇 - 把板子从「能启动」变成「能用」:Ethernet/SPI/MMC 三个纯接线外设
linux·github·嵌入式
七歌杜金房20 小时前
我终于又有了自己的 Linux 电脑
linux·debian·mac
神奇啊龙1 天前
我的第一个 TinyGo 项目:ESP32-C3 + DHT11 + SSD1306
物联网·嵌入式
tntxia2 天前
linux curl命令详解_curl详解
linux
扛枪的书生2 天前
Linux 网络管理器用法速查
linux
顺风尿一寸2 天前
Java Socket 内核之旅:从 SocketChannel.read() 到 tcp_recvmsg 与 epoll 的完整调用链路
linux
XIAOHEZIcode2 天前
Ubuntu 终端美化全栈指南:Bash 到 Kitty 踩坑实录
linux·ubuntu·命令行
比老马还六2 天前
Bipes-Blockly项目二次开发/Coze智能体(十)
前端·嵌入式