2. 开发环境搭建
在开始编写 Linux 内核摄像头驱动之前,必须搭建一套完整、可复现的开发环境。本章将指导你完成四个核心步骤:建立内核源码树、配置交叉编译工具链、安装 QEMU 模拟器与根文件系统,以及准备摄像头硬件(物理 USB 摄像头或虚拟 vivid 驱动)。这套环境将贯穿整个课程,确保你可以在主机上编译、在模拟器或目标板上调试摄像头驱动。
环境概览: 本课程使用 x86_64 主机 + ARM 交叉编译 + QEMU 模拟 ARM 开发板。摄像头部分同时支持物理 USB 摄像头(UVC 协议)和内核虚拟摄像头驱动 vivid,后者无需硬件即可调试 V4L2 框架。
2.1 搭建 Linux 内核源码树
内核源码树是驱动开发的基础。你需要下载、配置并编译一份与目标平台匹配的内核源码。推荐使用长期支持(LTS)版本,例如 6.1.y 或 5.15.y,以获得稳定的 V4L2 API。
2.1.1 下载与解压
# 下载 LTS 内核(以 6.1.55 为例)
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.55.tar.xz
# 解压
tar -xf linux-6.1.55.tar.xz
cd linux-6.1.55
2.1.2 配置内核选项
摄像头驱动开发需要启用以下关键配置项:
| 配置项 | 说明 | 推荐值 |
|---|---|---|
CONFIG_MEDIA_SUPPORT |
多媒体框架总开关 | y |
CONFIG_MEDIA_CAMERA_SUPPORT |
摄像头子类支持 | y |
CONFIG_V4L2 |
V4L2 核心 API | y |
CONFIG_VIDEO_V4L2 |
V4L2 视频设备 | y |
CONFIG_VIDEO_VIVID |
虚拟摄像头驱动(调试用) | m |
CONFIG_USB_VIDEO_CLASS |
UVC(USB Video Class)驱动 | m |
# 使用 defconfig 生成基础配置,然后手动调整
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig
# 进入菜单配置界面
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
# 在菜单中依次进入:
# Device Drivers → Multimedia support → Media core support → [*] Video for Linux
# Device Drivers → Multimedia support → Camera sensors → <M> Virtual test driver (vivid)
# Device Drivers → Multimedia support → USB Video Class (UVC) → <M> USB Video Class
常见错误: 如果忘记启用
CONFIG_MEDIA_CAMERA_SUPPORT,V4L2 设备节点将无法创建。编译前务必检查 .config 文件中该选项是否为 y。
2.1.3 编译内核与设备树
# 编译内核镜像(zImage)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage -j$(nproc)
# 编译设备树(以 vexpress-a9 为例)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- vexpress-v2p-ca9.dtb
# 编译内核模块(包括 vivid 和 uvcvideo)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules -j$(nproc)
2.2 配置交叉编译工具链
交叉编译工具链允许你在 x86_64 主机上编译出 ARM 架构的可执行文件。本课程选用 arm-linux-gnueabihf 工具链,支持硬件浮点运算,适合 ARM Cortex-A9 系列处理器。
2.2.1 安装工具链
# 使用 apt 安装(Ubuntu/Debian)
sudo apt-get install gcc-arm-linux-gnueabihf binutils-arm-linux-gnueabihf
# 验证安装
arm-linux-gnueabihf-gcc --version
# 输出示例:arm-linux-gnueabihf-gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
2.2.2 工具链组件清单
| 组件 | 命令 | 用途 |
|---|---|---|
| C 编译器 | arm-linux-gnueabihf-gcc |
编译 C 源码 |
| 汇编器 | arm-linux-gnueabihf-as |
汇编语言处理 |
| 链接器 | arm-linux-gnueabihf-ld |
目标文件链接 |
| 目标文件工具 | arm-linux-gnueabihf-objcopy |
格式转换(如 ELF → 裸二进制) |
| 反汇编器 | arm-linux-gnueabihf-objdump |
调试与代码分析 |
2.2.3 测试编译
# 编写一个简单的测试程序
echo '#include <stdio.h>
int main() { printf("Hello, Camera Driver!\n"); return 0; }' > test.c
# 交叉编译
arm-linux-gnueabihf-gcc -o test_arm test.c
# 查看文件类型(确认是 ARM 架构)
file test_arm
# 输出:test_arm: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3
关键点: 交叉编译工具链的前缀(
arm-linux-gnueabihf-)必须与内核编译时使用的 CROSS_COMPILE 变量完全一致。如果使用其他前缀(如 arm-none-linux-gnueabihf-),需要同步修改所有编译命令。
2.3 安装 QEMU 模拟器与根文件系统
QEMU 可以模拟 ARM 开发板(如 vexpress-a9),让你在没有物理硬件的情况下运行内核和驱动。根文件系统提供用户空间环境,用于加载驱动模块和运行测试工具。
2.3.1 安装 QEMU
# 安装 QEMU 系统模拟器(支持 ARM)
sudo apt-get install qemu-system-arm
# 验证安装
qemu-system-arm --version
# 输出示例:QEMU emulator version 7.2.0
2.3.2 制作根文件系统
使用 BusyBox 构建最小根文件系统:
# 下载 BusyBox
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar -xf busybox-1.36.1.tar.bz2
cd busybox-1.36.1
# 配置为静态编译(避免动态库依赖)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
# 进入 Settings → Build static binary (no shared libs) → 选中
# 编译并安装
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j$(nproc)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- install CONFIG_PREFIX=../rootfs
# 创建必要目录
cd ../rootfs
mkdir -p proc sys dev etc/init.d lib/modules
# 创建初始化脚本
echo '#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mdev -s
echo "Welcome to Camera Driver QEMU Environment"
/bin/sh' > etc/init.d/rcS
chmod +x etc/init.d/rcS
# 打包为 ext4 镜像
dd if=/dev/zero of=rootfs.ext4 bs=1M count=64
mkfs.ext4 rootfs.ext4
sudo mount -o loop rootfs.ext4 /mnt
sudo cp -r * /mnt/
sudo umount /mnt
2.3.3 启动 QEMU 并验证
# 启动 QEMU(挂载内核、设备树和根文件系统)
qemu-system-arm \
-M vexpress-a9 \
-kernel ../linux-6.1.55/arch/arm/boot/zImage \
-dtb ../linux-6.1.55/arch/arm/boot/dts/vexpress-v2p-ca9.dtb \
-drive file=rootfs.ext4,format=raw,if=sd \
-append "root=/dev/mmcblk0 console=ttyAMA0" \
-nographic
# 在 QEMU 控制台中验证内核版本
# cat /proc/version
# Linux version 6.1.55 ... arm-linux-gnueabihf-gcc-11
2.4 准备摄像头硬件
摄像头驱动开发需要测试硬件。本课程提供两种方案:物理 USB 摄像头(UVC 协议)和 虚拟摄像头 vivid(内核自带测试驱动)。推荐初学者优先使用 vivid,因为它无需硬件、可完全在 QEMU 中模拟。
2.4.1 方案一:虚拟摄像头 vivid
vivid 是内核自带的虚拟视频驱动,可以模拟多种摄像头功能(如 MJPEG、YUYV 格式、帧率控制等)。
# 在 QEMU 客户机中加载 vivid 模块
insmod /lib/modules/6.1.55/kernel/drivers/media/test-drivers/vivid.ko
# 检查设备节点
ls /dev/video*
# 输出:/dev/video0 /dev/video1 /dev/video2 /dev/video3
# 使用 v4l2-ctl 查看能力
v4l2-ctl -d /dev/video0 --all
# 输出示例:
# Driver Info:
# Driver name : vivid
# Card type : vivid
# Bus info : platform:vivid
# Media controller : yes
| 特性 | vivid 支持 | 说明 |
|---|---|---|
| 视频格式 | YUYV、MJPEG、RGB24 等 | 可配置输出格式 |
| 分辨率 | 可编程(默认 640×480) | 通过模块参数调整 |
| 帧率 | 1~60 fps | 模拟真实摄像头时序 |
| 多设备 | 最多 4 个视频节点 | 模拟多摄像头场景 |
| 控制接口 | 亮度、对比度、饱和度等 | 标准 V4L2 控制 |
2.4.2 方案二:物理 USB 摄像头(UVC)
如果使用物理 USB 摄像头,需要确保主机 USB 设备可以透传给 QEMU 客户机。
# 在主机上查看 USB 摄像头设备
lsusb
# 输出示例:Bus 001 Device 004: ID 046d:0825 Logitech, Inc. Webcam C270
# 启动 QEMU 时透传 USB 设备
qemu-system-arm \
-M vexpress-a9 \
-kernel ../linux-6.1.55/arch/arm/boot/zImage \
-dtb ../linux-6.1.55/arch/arm/boot/dts/vexpress-v2p-ca9.dtb \
-drive file=rootfs.ext4,format=raw,if=sd \
-append "root=/dev/mmcblk0 console=ttyAMA0" \
-usb -device usb-host,vendorid=0x046d,productid=0x0825 \
-nographic
# 在客户机中加载 UVC 驱动
insmod /lib/modules/6.1.55/kernel/drivers/media/usb/uvc/uvcvideo.ko
# 检查设备
ls /dev/video*
# 输出:/dev/video0(USB 摄像头)
USB 透传注意事项: QEMU 的 USB 透传需要主机权限。如果遇到
Could not add USB device 错误,请确保当前用户属于 plugdev 组,或者使用 sudo 启动 QEMU。另外,某些 USB 摄像头需要额外的固件,请确认内核配置中启用了 CONFIG_USB_VIDEO_CLASS 和 CONFIG_USB_VIDEO_CLASS_INPUT。
2.4.3 验证摄像头工作状态
# 安装 v4l-utils(在根文件系统中预编译)
# 在主机上交叉编译 v4l-utils
git clone https://git.linuxtv.org/v4l-utils.git
cd v4l-utils
./configure --host=arm-linux-gnueabihf --prefix=/usr/arm
make -j$(nproc)
make install DESTDIR=../rootfs
# 在 QEMU 客户机中测试
v4l2-ctl --list-devices
# 输出 vivid 或 UVC 设备信息
# 抓取一帧图像
v4l2-ctl -d /dev/video0 --set-fmt-video=width=640,height=480,pixelformat=YUYV
v4l2-ctl -d /dev/video0 --stream-mmap --stream-to=frame.yuv --stream-count=1
# 检查文件大小(640*480*2 = 614400 字节)
ls -l frame.yuv
2.5 环境验证清单
完成上述步骤后,使用以下清单确认环境就绪:
| 检查项 | 验证命令 | 预期结果 |
|---|---|---|
| 交叉编译器可用 | arm-linux-gnueabihf-gcc --version |
显示版本号(如 11.3.0) |
| 内核编译成功 | ls arch/arm/boot/zImage |
文件存在且非空 |
| 设备树编译成功 | ls arch/arm/boot/dts/vexpress-v2p-ca9.dtb |
文件存在 |
| QEMU 可启动 | qemu-system-arm ... |
进入 shell 提示符 |
| vivid 模块加载 | ls /dev/video* |
至少出现 /dev/video0 |
| v4l2-ctl 可用 | v4l2-ctl --list-devices |
显示 vivid 或 UVC 设备信息 |
环境就绪标志: 当你可以在 QEMU 客户机中执行
v4l2-ctl -d /dev/video0 --all 并看到完整的驱动信息时,说明开发环境已完全搭建成功。接下来就可以进入第 3 章,开始编写第一个 V4L2 摄像头驱动。