2. 嵌入式Linux基础

在 Qualcomm Linux BSP 开发中,理解嵌入式 Linux 的整体骨架,比记住某一条编译命令更重要。BSP 工程师面对的不是单一程序,而是一套从芯片上电到用户空间服务可用的完整链路:引导加载程序初始化 DDR 与时钟,内核接管 CPU 与中断,驱动通过设备树获得板级硬件描述,根文件系统提供用户态运行环境,最终由 init 拉起业务进程。本节围绕这条主线,梳理内核架构、设备驱动模型、设备树、根文件系统与启动流程五个关键对象及其相互关系。

一、Linux 内核架构:分层与职责边界

Linux 内核在逻辑上可分为用户接口层、系统调用层、核心子系统层和硬件抽象层。用户程序通过系统调用进入内核态;VFS、网络协议栈、内存管理、进程调度、中断管理等核心子系统提供通用能力;具体硬件差异则由体系结构相关代码和驱动屏蔽。对 BSP 而言,最常改动的是驱动、设备树和板级配置,而不是核心调度器。

  • 进程与调度:CFS 负责普通进程公平调度,实时策略用于音视频、工业控制等时延敏感场景。
  • 内存管理:MMU 完成虚拟地址到物理地址映射,kmalloc/vmalloc 服务于内核,用户态通过页表隔离。
  • VFS 与文件系统:向上提供统一 open/read/write/ioctl 接口,向下适配 ext4、f2fs、ubifs、squashfs 等。
  • 设备驱动:把寄存器、中断、DMA、时钟、电源等硬件细节封装成内核对象。

二、设备驱动模型:总线、设备与驱动

Linux 2.6 之后引入统一设备模型,核心是 kobject、kset、device、driver、bus 以及 sysfs。设备与驱动注册到同一条总线上,由总线负责匹配;匹配成功后调用驱动的 probe,失败则调用 remove。Qualcomm 平台常见总线包括 platform、I2C、SPI、PCIe、USB 和 RPMSG 等。platform 总线用于描述 SoC 内部集成外设,如 I2C 控制器、GPIO 控制器、时钟控制器和电源管理 IC。

/* 典型 platform 驱动骨架 */
static int qcom_sample_probe(struct platform_device *pdev)
{
    struct resource *res;
    void __iomem *base;

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    base = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(base))
        return PTR_ERR(base);

    dev_info(&pdev->dev, "sample device probed\n");
    return 0;
}

static const struct of_device_id qcom_sample_of_match[] = {
    { .compatible = "qcom,sample-device" },
    { }
};
MODULE_DEVICE_TABLE(of, qcom_sample_of_match);

static struct platform_driver qcom_sample_driver = {
    .probe = qcom_sample_probe,
    .driver = {
        .name = "qcom-sample",
        .of_match_table = qcom_sample_of_match,
    },
};
module_platform_driver(qcom_sample_driver);

驱动模型的价值在于解耦:设备树描述“有什么硬件、接在哪里、用什么参数”,驱动描述“如何操作这类硬件”。同一驱动可服务多块板卡,板级差异通过 compatible、reg、interrupts、clocks、pinctrl 等属性传入。

三、设备树:板级硬件的声明式描述

设备树源文件 DTS 经 DTC 编译为 DTB,由 bootloader 传给内核。内核解析后生成 device_node 与 platform_device,再与驱动匹配。Qualcomm 平台通常采用多级包含:SoC 级 dtsi 描述公共控制器,板级 dts 覆盖引脚、电源、外设使能状态。常用属性包括 compatible、reg、interrupts、clocks、clock-names、pinctrl-0、status。

&i2c_1 {
    status = "okay";
    clock-frequency = <400000>;

    touch@38 {
        compatible = "goodix,gt911";
        reg = <0x38>;
        interrupt-parent = <&tlmm>;
        interrupts = <45 IRQ_TYPE_EDGE_FALLING>;
        reset-gpios = <&tlmm 12 GPIO_ACTIVE_LOW>;
    };
};
调试设备树时,优先检查 status 是否为 okay、reg 地址是否与原理图一致、中断号和 GPIO 是否被其他节点占用。可通过 /proc/device-tree 与 dmesg 中的 of_ 日志交叉验证。

四、根文件系统与启动流程

根文件系统是内核挂载后用户态程序的载体,包含 /bin、/sbin、/lib、/etc、/dev、/proc、/sys 等目录。嵌入式场景常用只读 squashfs 加可写 overlay,或 ubifs、ext4、f2fs。Qualcomm 设备通常由 ABL/XBL 引导,加载 boot.img 中的 kernel、dtb 与 ramdisk,再切换到 system/vendor 分区。

阶段执行主体关键动作常见问题
上电复位Boot ROM校验并加载 XBL签名、熔丝配置错误
引导加载XBL/ABL初始化 DDR、时钟,加载 boot.imgDDR 参数、分区表异常
内核启动Linux kernel解压、解析 DTB、初始化子系统DTB 不匹配、驱动 probe 失败
挂载根文件系统kernel + initramfs挂载 rootfs,执行 /init分区损坏、挂载参数错误
用户态初始化init/systemd拉起服务、配置网络与显示服务依赖、权限问题

五、案例与复盘:触摸屏不工作的排查路径

某 Qualcomm 板卡触摸屏无响应。按启动链路逐层排查:先确认 dmesg 中 I2C 控制器是否注册成功;再检查设备树 touch 节点 status 是否为 okay、reg 是否为 0x38;随后用 i2cdetect 扫描总线,确认器件是否应答;若应答正常但无中断,检查 pinctrl 与 interrupts 配置;最后确认驱动是否编入内核或作为模块加载。该案例说明,BSP 问题往往不是单点故障,而是设备树、驱动、时钟、电源与用户态服务共同作用的结果。

常见误区:只改驱动不看设备树;只改设备树不验证时钟与电源;把根文件系统问题误判为内核问题。建议始终按“硬件描述—驱动匹配—资源申请—用户态接口”的顺序定位。
嵌入式 Linux 启动链路与内核对象关系 Boot ROM 上电校验 XBL / ABL DDR 时钟初始化 Kernel 解压与子系统初始化 DTB 解析 生成 platform_device Rootfs 挂载并执行 init 用户态服务 业务进程 Linux 内核分层 用户接口层:系统调用 / VFS / 网络套接字 / 字符设备节点 核心子系统:进程调度 / 内存管理 / 中断管理 / 网络协议栈 / 文件系统 硬件抽象层:体系结构代码 / 设备驱动 / 总线模型 / 电源与时钟框架 设备驱动模型 bus platform/i2c/spi device 来自设备树 driver probe/remove 匹配依据:compatible / of_device_id / id_table 匹配成功 → probe → 申请资源 → 注册字符/块/网络设备 设备树与根文件系统 DTS / DTSI 板级硬件描述 DTB 由 bootloader 传递 根文件系统:/bin /sbin /lib /etc /dev /proc /sys 常见格式:squashfs + overlay、ubifs、ext4、f2fs

综上,Qualcomm Linux BSP 开发的核心能力,是沿着“引导—内核—设备树—驱动—根文件系统—用户态”这条链路建立系统观。只有把每个阶段的输入、输出和失败表现对应起来,才能在面对启动失败、外设不识别、服务起不来等问题时快速缩小范围,而不是盲目重编内核或反复刷机。