2、开发环境搭建:交叉编译工具链、内核源码获取与配置、设备树基础、QEMU模拟环境搭建

好,咱们正式开始动手了。这一章我带你搭好开发环境。说实话,很多初学者卡就卡在这一步——工具链没配好,内核编不过,QEMU起不来,然后就没然后了。我当年也踩过不少坑,今天我把这些经验都摊开来讲。

2.1 交叉编译工具链:为什么需要它?

你想想看,你的电脑是x86架构,而嵌入式设备通常是ARM、RISC-V或者MIPS。x86的二进制程序,ARM芯片根本看不懂。所以我们需要交叉编译工具链——在x86主机上编译出ARM能运行的程序。

我个人习惯用Linaro提供的预编译工具链,稳定又省事。以ARM64(AArch64)为例:

# 下载ARM64交叉编译工具链
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/aarch64-linux-gnu/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz

# 解压到 /opt 目录
sudo tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz -C /opt/

# 添加环境变量
export PATH=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin

验证是否安装成功:

aarch64-linux-gnu-gcc --version

看到版本号就对了。嗯,这里要注意——工具链版本要和内核版本匹配。太老的gcc编新内核会报错,太新的gcc编老内核也可能出问题。我在项目中遇到过gcc 10编译Linux 4.19内核,结果一堆-Werror警告,折腾了半天。

我的建议:如果你刚开始学,直接用Linaro 7.5版本,搭配Linux 5.10 LTS内核,这对组合非常稳。

2.2 内核源码获取与配置

源码从哪来?当然是kernel.org。但我建议你直接用LTS版本,长期支持,社区维护积极。

# 下载Linux 5.10 LTS内核
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.200.tar.xz

# 解压
tar -xvf linux-5.10.200.tar.xz
cd linux-5.10.200

配置内核有两种方式:

  • make menuconfig:基于ncurses的图形界面,最常用
  • make defconfig:使用默认配置,适合快速验证

对于Camera子系统,我们需要关注这些配置项:

配置项 说明 建议值
CONFIG_MEDIA_SUPPORT 多媒体框架支持 y
CONFIG_MEDIA_CAMERA_SUPPORT Camera设备支持 y
CONFIG_V4L2 Video4Linux2核心 y
CONFIG_VIDEO_V4L2_SUBDEV_API 子设备API y

配置命令:

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig

进入menuconfig后,按/键可以搜索配置项。比如搜"V4L2",所有相关选项就出来了。这个技巧很实用,我经常用。

曾经踩过的坑:有一次我忘了设置CROSS_COMPILE,直接make menuconfig,结果配置界面倒是出来了,但编译时用的本地gcc,生成的是x86的vmlinux。放到QEMU里直接崩溃。所以每次编译前,务必确认ARCH和CROSS_COMPILE都设对了。

2.3 设备树基础:硬件描述语言

设备树(Device Tree)说白了就是描述硬件信息的配置文件。以前内核把硬件信息硬编码在arch/arm/mach-xxx目录下,后来大家发现这样太乱了——换一个板子就要改内核代码。于是设备树诞生了。

设备树文件有两种:

  • .dts:设备树源文件,人类可读的文本
  • .dtb:编译后的二进制文件,内核直接使用

一个简单的Camera节点示例:

/dts-v1/;

/ {
    model = "QEMU ARM64 Virtual Machine";
    compatible = "qemu, virt";

    soc {
        #address-cells = <2>;
        #size-cells = <2>;

        camera@1c00000 {
            compatible = "virtio,mmio";
            reg = <0x0 0x1c00000 0x0 0x1000>;
            interrupts = <0 42 4>;
            status = "okay";
        };
    };
};

编译设备树:

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- dtbs

生成的dtb文件在arch/arm64/boot/dts/目录下。

核心概念:设备树中的每个节点代表一个硬件设备。compatible属性用来匹配驱动,reg属性描述寄存器地址和大小,interrupts描述中断号。这些信息驱动会通过of_match_table来获取。

2.4 QEMU模拟环境搭建

QEMU是我们最好的朋友。没有开发板?没关系,QEMU可以模拟ARM64硬件,跑完整的内核和根文件系统。

安装QEMU:

sudo apt-get install qemu-system-arm qemu-utils

编译内核和设备树:

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- dtbs

制作根文件系统(使用BusyBox):

# 下载BusyBox
wget https://busybox.net/downloads/busybox-1.36.0.tar.bz2
tar -xvf busybox-1.36.0.tar.bz2
cd busybox-1.36.0

# 配置并编译
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig
# 确保选中 "Build static binary (no shared libs)"
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- install

# 制作根文件系统镜像
cd _install
mkdir proc sys dev etc etc/init.d
cat > etc/init.d/rcS << EOF
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
/sbin/mdev -s
EOF
chmod +x etc/init.d/rcS

# 打包成cpio
find . | cpio -o --format=newc > ../rootfs.cpio
cd ..
gzip -c rootfs.cpio > rootfs.cpio.gz

启动QEMU:

qemu-system-aarch64 \
    -machine virt \
    -cpu cortex-a57 \
    -kernel arch/arm64/boot/Image \
    -initrd /path/to/rootfs.cpio.gz \
    -append "console=ttyAMA0 root=/dev/ram rdinit=/sbin/init" \
    -nographic \
    -device virtio-gpu-pci \
    -device virtio-mmio

看到内核启动日志,最后出现shell提示符,就说明环境搭建成功了。

调试小技巧:加上-s -S参数可以启动GDB调试。在另一个终端运行aarch64-linux-gnu-gdb vmlinux,然后输入target remote :1234,就可以单步调试内核了。这个我在调试Camera驱动时用过无数次,非常管用。

2.5 验证Camera子系统是否启用

启动QEMU后,进入系统,执行以下命令检查V4L2设备:

# 查看V4L2设备列表
ls /dev/video*

# 查看设备信息
v4l2-ctl --list-devices

# 查看支持的格式
v4l2-ctl -d /dev/video0 --list-formats

如果能看到设备节点,说明我们的内核配置正确,Camera子系统已经就绪了。

曾经踩过的坑:有一次我编译内核时忘了加CONFIG_VIRTIO相关选项,结果QEMU模拟的virtio设备内核根本不认。折腾了两天才发现是内核配置漏了。所以建议你用我上面给的配置表,逐项核对。

好,环境搭建就到这里。下一章我们开始深入V4L2框架,看看Camera设备在内核中是怎么被管理的。到时候我会结合代码,一步步带你分析。