一、准备工作
内核驱动程序的编译依赖于对应体系(arm/x86)的内核模块(对应工具链(gcc)编译过的(make modules)的内核源码),因此开始编写驱动程序之前,需要做一些工作以具备驱动程序编译条件
1.1 开发环境
参考上一篇“树莓派添加驱动程序(一)”
- 搭建主机(win7+vmware+raspbian buster) + 目标机(树莓派4b + raspbian buster)
- 在主机上添加“交叉编译工具链”
- 下载对应版本的内核源代码 “linux-rpi-4.19.y.zip”
1.2 编译目标体系内核源代码
- 解压并编译为x86版本 linux-rpi-4.19.y-x86
- make x86_64_defconfig
- make modules (或者 make bzImage modules dtbs但是编译时间特别长,编译驱动只需要make modules就够了)
- 编译为arm版本 linux-rpi-4.19.y-arm
- make bcm2709_defconfig
- make zImage modules dtbs
二、编写第一个驱动程序hello
hello.c
#include <linux/init.h>
#include <linux/module.h>
static int __init hello_init(void)
{
printk(KERN_ALERT"Hello ccsens!\n");
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_ALERT"Goodbye ccsens\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("zhangsan");
2.1 使用主机自身源码进行编译
KERNELDIR := /lib/modules/uname -r
/build 使用主机自身模块
ifeq ($(KERNELRELEASE),)
KERNELDIR := /lib/modules/`uname -r`/build
PWD := $(shell pwd)
ARCH = x86
CROSS_COMPILE =
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE)
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE)
clean:
rm -rf *.o *~core.depend .*.cmd *.mod.c *.tmp_version *.order *.symvers
else
obj-m := hello.o
endif
查看编译生成的模块信息
2.2 编译为x86版本(使用下载的内核源码)
修改x86版本的内核源码 KERNELDIR := /home/pi/linux-rpi-4.19.y-x86
ifeq ($(KERNELRELEASE),)
KERNELDIR := /home/pi/linux-rpi-4.19.y-x86
PWD := $(shell pwd)
ARCH = x86
CROSS_COMPILE =
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE)
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE)
clean:
rm -rf *.o *~core.depend .*.cmd *.mod.c *.tmp_version *.order *.symvers
else
obj-m := hello.o
endif
2.3 编译为arm版本(使用下载的内核源码)
选择arm版本的内核源码
ifeq ($(KERNELRELEASE),)
KERNELDIR := /home/pi/linux-rpi-4.19.y-arm
PWD := $(shell pwd)
ARCH = arm
CROSS_COMPILE = arm-linux-gnueabihf-
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE)
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE)
clean:
rm -rf *.o *~core.depend .*.cmd *.mod.c *.tmp_version *.order *.symvers
else
obj-m := hello.o
endif
三、遇到问题
3.1 Invalid Module Format
insmod: ERROR: could not insert module hello.ko: Invalid module format
dmesg查看内核输出
hello: version magic ‘4.19.97 SMP mod_unload ‘ should be ‘4.19.0-6-amd64 SMP mod_unload modversions ‘
发现是内核源码version不匹配,但是大版本是匹配的(4.19.x),由于是从github/raspberrypi官方仓库拉取的内核源码,可能是树莓派官网(https://www.raspberrypi.org)提供的下载镜像(raspbian buster)并没有和github上源码保持同步。
解决思路:
- 大版本一致所以不会有太大差异导致内核模块无法执行
- 修改源码版本号与目标系统保持一致
四、vermagic的组成
4.1 vermagic定义相关文件
在$KERNEL_SRC_DIR/include/linux/vermagic中定义了vermagic的组成
/* Simply sanity version stamp for modules. */
#ifdef CONFIG_SMP
#define MODULE_VERMAGIC_SMP "SMP "
#else
#define MODULE_VERMAGIC_SMP ""
#endif
#ifdef CONFIG_PREEMPT
#define MODULE_VERMAGIC_PREEMPT "preempt "
#else
#define MODULE_VERMAGIC_PREEMPT ""
#endif
#ifdef CONFIG_MODULE_UNLOAD
#define MODULE_VERMAGIC_MODULE_UNLOAD "mod_unload "
#else
#define MODULE_VERMAGIC_MODULE_UNLOAD ""
#endif
#ifdef CONFIG_MODVERSIONS
#define MODULE_VERMAGIC_MODVERSIONS "modversions "
#else
#define MODULE_VERMAGIC_MODVERSIONS ""
#endif
#ifndef MODULE_ARCH_VERMAGIC
#define MODULE_ARCH_VERMAGIC ""
#endif
#ifdef RANDSTRUCT_PLUGIN
#include <generated/randomize_layout_hash.h>
#define MODULE_RANDSTRUCT_PLUGIN "RANDSTRUCT_PLUGIN_" RANDSTRUCT_HASHED_SEED
#else
#define MODULE_RANDSTRUCT_PLUGIN
#endif
#define VERMAGIC_STRING \
UTS_RELEASE " " \
MODULE_VERMAGIC_SMP MODULE_VERMAGIC_PREEMPT \
MODULE_VERMAGIC_MODULE_UNLOAD MODUcLE_VERMAGIC_MODVERSIONS \
MODULE_ARCH_VERMAGIC \
MODULE_RANDSTRUCT_PLUGIN
其中的UTS_RELEASE代表内核的版本号,
在$KERNEL_SRC_DIR/include/generated/utsrelease.h中定义
define UTS_RELEASE "4.19.0-6-amd64"
utsrelease.h在编译阶段动态生成,
生成内容由$KERNEL_SRC_DIR/Makefile指定
# SPDX-License-Identifier: GPL-2.0
VERSION = 4
PATCHLEVEL = 19
SUBLEVEL = 0
EXTRAVERSION = -6-amd64
NAME = "People's Front"
4.2 vermagic举例
4.19.0-6-amd64 SMP mod_unload modversions
VERSION: 4
PATCHLEVEL: 19
SUBLEVEL: 0
EXTRAVERSION: -6-amd64
MODULE_VERMAGIC_SMP: “SMP “
MODULE_VERMAGIC_MODULE_UNLOAD: “mod_unload “
MODULE_VERMAGIC_MODVERSIONS “modversions “
其中 VERSION,PATHCHLEVEL,SUBLEVEL,EXTRAVERSION 可以在Makefile中修改
其他信息在 $KERNEL_SRC_DIR/include/linux/vermagic.h中定义
4.3 vermagic的修改
方法一:通过makemenuconfig修改
vermagic相关文件中的内容都是由makemenuconfig动态生成(决定)的,最好的办法是通过make menuconfig修改信息。比如:
内核版本可以在: General setup-> Build ID Salt中找到
vermagic其他信息可以在: Enable loadable module support 中找到
方法二:修改相关文件中的定义
有时候,是在找不到对应的make menuconfig配置,或者即使找到了,因为多出关联的原因改了没起作用,暴力办法可以通过直接修改相关文件来修改vermagic。
Makefile中修改版本号
include/linux/vermagic.h中修改其他信息