解决编译小米Max内核源码后,WiFi驱动模块不能启动的问题

本文详细记录了解决小米Max内核WiFi驱动加载问题的过程,包括下载高通QAEP源码、配置环境、源码修改与编译步骤,适用于对内核开发感兴趣的读者。

去年,在成功编译小米Max的内核源码后,刷入手机发现WiFi驱动并不能正常加载,通过一翻操作后,成功启动WiFi驱动。这里分享一下我完整的解决过程。

 

小米FAQ已经给出原因:

 

因为WiFi驱动模块是由高通开发的,不是小米开发的,所以不在小米的开源仓库里面,需要去下载高通的开源项目QAEP。WiFi模块源码在QAEP项目里的vendor/qcom/opensource/wlan/prima

 

 

腾讯云新建一台按量计费的香港服务器,用完可以立刻销毁。

由于高通源码巨大,本人下载小米Max的有115G,所以新建的时候推荐选500G硬盘大小

 

安装下载环境

yum install -y git curl screen
curl https://storage.googleapis.com/git-repo-downloads/repo > /bin/repo
chmod 777 /bin/repo

 

启动screen来后台开一个shell下载,防止下载过程中SSH突然掉线

screen -S test

执行后会来到一个全新的SSH界面(其他screen操作详情请百度)

 

高通QAEP源码的下载命令为

$ repo init -u git://codeaurora.org/platform/manifest.git -b [branch] -m [manifest] --repo-url=git://codeaurora.org/tools/repo.git --repo-branch=caf-stable

其中branch的分支应该选择release,manifest选$tag.xml

例如小米4的cancro-kk-oss分支:

~/Qualcomm_Android$ repo init -u git://codeaurora.org/platform/manifest.git -b release -m LNX.LA.3.5.2.2.1-04310-8x74.0.xml --repo-url=git://codeaurora.org/tools/repo.git --repo-branch=caf-stable

所以这里我的命令为:

mkdir /myDownload cd /myDownload repo init -u git://codeaurora.org/platform/manifest.git -b release -m LA.BR.1.3.4-05310-8976.0.xml --repo-url=git://codeaurora.org/tools/repo.git --repo-branch=caf-stable

然后执行同步命令(下载代码)

~/Qualcomm_Android$ repo sync -j8

FYI:高通这个仓库真的很大,下载需要大量时间(本人腾讯云香港下了16个小时)

repo sync -f -j4 --no-tags --no-clone-bundle 
// --no-tags:减少不需要的tag下载,可以缩减下载的代码量 
// -f:当某个库因为网络原因货其他原因下载失败的时候可以继续进行,避免已经下载的代码不能写入到硬上 
// -j4:开启4个线程来下载,这个根据CPU和硬盘的性能决定的

附小米原文

高通源码下载完成后

取出vendor/qcom/opensource/wlan文件夹

其中.git文件夹比较占空间,而且也无用,可以删除

打开Makefile发现是这样的

这怎么编译?别慌,小修改即可

1.首先将Kbuild文件内容全部复制到Makefile的头部

2.将头部的两段代码删除,删除原因在英文注释里,我们是编译成外部wlan.ko模块文件,所以不需要这两段

 

然后在头部补上一句

KERNEL_BUILD := 0

 

 

3.在Makefile尾部的这里4句屏蔽掉,因为我们要重新写一个obj-m出来

 

 

4.将Makefile尾巴这两句改写成正常的格式,并放在Makefile文件头部

 

WLAN_ROOT := $(PWD)
MODNAME := wlan
KBUILD_OPTIONS := WLAN_ROOT
KBUILD_OPTIONS += MODNAME

5.将Makefile底部重写增加obj-m

 

#加入这句话的目的是为了接下来要查看编译器的预定义有哪些
$(info info $(CDEFINES))
ifneq ($(KERNELRELEASE),)    
	$(MODNAME)-objs:=$(OBJS)
	obj-m := $(MODNAME).o
else
	KERNEL_SOURCE := /hydrogen-m-oss/output
all:    
	$(MAKE) $(EXTRA_CFLAGS)  -C $(KERNEL_SOURCE)  SUBDIRS=$(CURDIR) ARCH=arm64 \
	CROSS_COMPILE=aarch64-linux-android- M=$(PWD) modules 
clean:    
	rm -f *.ko *.o *.mod.o *.mod.c *.symvers    
endif

主要是多个了obj-m来指定生成的模块

至此Makefile修改完成

 

重新make一下

会发现还是有错,提示某些未定义。

这时候

$(info info $(CDEFINES))

那句话就派上用场了

 

可以看到编译器总共有哪些定义,格式为-DXXXXXXXXXXX

我复制下来,将"-D"替换成"#define "后如下

要在哪些地方加上这些定义呢?答:每个头文件最顶部,当然我不可能一个一个手动加进去,

用一些技巧就好了

手动搜索出*.h结尾的头文件,新建一个文件夹,全部复制出来,

 

我全部扔进notepad++打开,并发现每个头文件顶部都有一个Copyright (c)字样。

 

这就好办了

先把每个文件的Copyright (c)替换成这样,

再最终替换

替换完成

注意要加上,这个好像是必须的,不加编译不通过

#define WCN_PRONTO

 

替换完成后务必检查替换进去的#define定义数量是否完整,因为notepad++替换文本有长度限制

 

如果完整,则可以按全部保存。

接下来把替换完毕的.h头文件放入Kernel/include目录下(防止编译wifi驱动时候编译器找不到他们)

重新make,即可编译成功

 

<think>我们正在讨论如何编译内核源码中的BPF示例程序。BPF(Berkeley Packet Filter)现在通常指eBPF(extended BPF),它是一种在内核中运行沙盒程序的技术,无需修改内核源代码或加载内核模块内核源码中通常包含一些BPF示例程序,这些示例位于`samples/bpf/`目录下。编译这些示例需要一些前提条件,比如安装必要的工具链(如clang、llvm等)以及内核头文件等。 以下是编译内核源码中BPF示例的一般步骤: 1. **获取内核源码**:首先需要获取内核源代码,可以从[kernel.org](https://www.kernel.org/)下载,或者使用git克隆Linux内核的git仓库。 2. **配置内核**:进入内核源码目录,使用已有的配置(如当前运行内核的配置)或进行新的配置。通常,需要确保配置中启用了BPF相关的选项。例如: - `CONFIG_BPF=y` - `CONFIG_BPF_SYSCALL=y` - `CONFIG_NET_CLS_BPF=y`(如果需要网络相关的示例) - 等等。 可以使用`make oldconfig`、`make menuconfig`等命令进行配置。 3. **编译内核**:虽然编译示例不需要完全编译整个内核,但需要编译生成内核头文件和一些必要的模块。通常,我们会先编译一次内核(或者至少编译好头文件)。 4. **编译BPF示例**:进入`samples/bpf/`目录,然后执行`make`命令。 但是,需要注意的是,从Linux 4.18版本开始,内核中的BPF示例程序被移动到`samples/bpf/`目录,并且编译方式有所改变。另外,由于BPF程序需要由clang编译为LLVM IR,然后通过LLVM的后端生成BPF字节码,因此需要安装clang(版本3.7以上)和LLVM(版本3.7以上)。 下面是一个详细的步骤示例(以Ubuntu系统为例): ### 步骤1:安装依赖 ```bash sudo apt update sudo apt install build-essential libncurses-dev bison flex libssl-dev libelf-dev clang llvm ``` ### 步骤2:获取内核源码 ```bash # 例如,我们使用5.10版本的内核 wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.tar.xz tar xvf linux-5.10.tar.xz cd linux-5.10 ``` ### 步骤3:配置内核 ```bash # 复制当前运行内核的配置 cp /boot/config-$(uname -r) .config # 或者使用默认配置 make defconfig # 确保BPF相关的配置已经开启,如果没有,可以通过make menuconfig来开启 # 在menuconfig中,可以搜索BPF相关选项并启用 make oldconfig ``` ### 步骤4:编译BPF示例 ```bash # 进入samples/bpf目录 cd samples/bpf/ # 编译 make ``` 在编译过程中,可能会遇到一些错误,比如缺少头文件等。这通常是因为没有正确设置内核头文件的路径。在`samples/bpf/Makefile`中,通常已经包含了正确的头文件路径设置,但如果你在编译整个内核之前编译示例,可能会出现问题。因此,建议先编译整个内核(至少编译出必要的头文件),然后再编译BPF示例。 另一种做法是,先编译整个内核(不安装),然后再编译示例: ```bash # 在内核源码根目录下 make -j$(nproc) # 然后进入samples/bpf/目录 cd samples/bpf/ make ``` 编译完成后,在`samples/bpf/`目录下会生成很多可执行文件和BPF字节码文件(以`.o`为后缀,这些文件包含BPF程序)。 ### 示例:编译并运行一个BPF程序 以`samples/bpf/`目录下的`tracex1_kern.c`和`tracex1_user.c`为例: - `tracex1_kern.c`:BPF内核部分,会被编译成BPF字节码。 - `tracex1_user.c`:用户空间程序,用于加载BPF程序并与内核交互。 编译后,会生成`tracex1_kern.o`(BPF字节码)和`tracex1`(用户空间程序)。运行`./tracex1`即可执行示例。 ### 注意事项 1. 确保你的系统已经安装了足够新版本的clang和LLVM(例如,clang版本至少为3.7,但建议使用较新版本,因为eBPF特性在不断更新)。 2. 如果遇到编译错误,检查错误信息,可能需要安装额外的依赖包或者调整Makefile。 3. 内核版本和示例程序版本要匹配,否则可能编译失败。 ### 代码解释 由于编译过程主要是通过Makefile进行,我们不需要编写代码,但可以看一下Makefile中的关键部分。在`samples/bpf/Makefile`中,会使用clang编译内核部分的BPF程序,并使用gcc编译用户空间程序。 例如,编译一个BPF内核程序(比如`xxx_kern.c`)的命令类似于: ```makefile clang -S \ -target bpf \ -D __KERNEL__ \ -D __ASM_SYSREG_H \ -Wall \ -Wno-unused-value \ -Wno-pointer-sign \ -Wno-compare-distinct-pointer-types \ -Werror \ -O2 -emit-llvm -c -g -o tmp_xxx_kern.ll xxx_kern.c llc -march=bpf -filetype=obj -o xxx_kern.o tmp_xxx_kern.ll ``` 而用户空间程序则使用gcc编译,并链接libbpf等库。 ### 相关问题 §§相关问题§§: 1. 如何编写一个简单的eBPF程序? 2. eBPF程序是如何被加载到内核中运行的? 3. 除了内核自带的示例,还有哪些常见的eBPF工具或项目? 4. 如何调试eBPF程序?</think>### 编译内核源码中的 BPF 示例程序 以下是详细步骤和代码解释,以 Linux 5.15 内核为例: --- #### 1. **安装依赖** ```bash # Ubuntu/Debian sudo apt update sudo apt install build-essential libncurses-dev bison flex libssl-dev libelf-dev clang llvm ``` #### 2. **获取内核源码** ```bash # 下载内核源码 (以 5.15 为例) wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.tar.xz tar -xvf linux-5.15.tar.xz cd linux-5.15 ``` #### 3. **配置内核** ```bash # 复制当前系统配置 cp /boot/config-$(uname -r) .config # 启用 BPF 相关选项 scripts/config --enable CONFIG_BPF scripts/config --enable CONFIG_BPF_SYSCALL scripts/config --module CONFIG_BPF_JIT # 生成配置 make olddefconfig ``` #### 4. **编译 BPF 示例** ```bash # 编译 samples/bpf/ 目录下的所有示例 make -C samples/bpf/ # 直接编译 BPF 示例 # 或从根目录编译 make samples/bpf/ # 完整编译时使用 ``` #### 5. **编译单个示例(以 tracex1 为例)** ```bash cd samples/bpf/ # 编译用户空间程序 + BPF 字节码 make tracex1 # 查看生成的文件 ls -lh tracex1 tracex1_kern.o ``` #### 关键代码解释 用户空间程序 (`tracex1_user.c`): ```c #include <bpf/libbpf.h> #include <bpf/bpf.h> int main() { struct bpf_object *obj; // 1. 加载 BPF 字节码 obj = bpf_object__open_file("tracex1_kern.o", NULL); bpf_object__load(obj); // 加载到内核 // 2. 附加到 kprobe struct bpf_program *prog = bpf_object__find_program_by_name(obj, "bpf_prog"); int prog_fd = bpf_program__fd(prog); bpf_program__attach(prog); // 附加到内核事件 // 3. 读取 BPF map 数据 int map_fd = bpf_object__find_map_fd_by_name(obj, "my_map"); while (1) { int key = 0, value; bpf_map_lookup_elem(map_fd, &key, &value); // 从 map 读取数据 printf("Counter: %d\n", value); sleep(1); } } ``` BPF 内核程序 (`tracex1_kern.c`): ```c #include <linux/bpf.h> #include <bpf/bpf_helpers.h> // 定义 BPF map(内核↔用户空间共享数据) struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(max_entries, 1); __type(key, u32); __type(value, int); } my_map SEC(".maps"); // kprobe 处理函数 SEC("kprobe/sys_execve") int bpf_prog(void *ctx) { u32 key = 0; int *value = bpf_map_lookup_elem(&my_map, &key); if (value) (*value)++; // 计数器自增 return 0; } char _license[] SEC("license") = "GPL"; // 必须声明许可证 ``` #### 6. **运行示例** ```bash # 需要 root 权限 sudo ./tracex1 # 输出示例(每秒打印 sys_execve 调用次数): Counter: 12 Counter: 15 Counter: 18 ``` --- ### 关键概念说明 1. **BPF 字节码生成**: - Clang 将 `.c` 文件编译为 ELF 格式的 `.o` 文件(包含 BPF 指令) - 通过 `objdump -d tracex1_kern.o` 可查看字节码 2. **加载过程**: ```mermaid graph LR A[BPF C代码] -->|Clang编译| B[BPF字节码.o] B -->|bpf_object__load| C[内核验证器] C -->|通过后| D[内核JIT编译] D -->|执行| E[内核事件触发] ``` 3. **调试技巧**: ```bash # 查看 BPF 程序日志 sudo cat /sys/kernel/debug/tracing/trace_pipe # 检查程序状态 bpftool prog show ``` ---
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值