2. SA8155 BSP源码获取与目录结构解析

任务目标

在SA8155平台上,将一个GPIO引脚配置为输入模式并绑定按键,启用该GPIO的中断功能。当按键按下(电平变化)时,触发中断服务函数,在回调中打印按键状态并清除中断标志,完成一次完整的中断处理流程。

环境与依赖

  • 硬件:SA8155开发板,按键连接至GPIO(本例使用GPIO_58,低电平有效)
  • 软件:Linux内核源码树,已配置好交叉编译工具链
  • 依赖头文件:linux/gpio.h、linux/interrupt.h、linux/of_gpio.h

实现原理

本案例基于Linux内核的GPIO子系统与中断子系统。通过设备树声明GPIO引脚属性,驱动中调用gpio_request()申请引脚,gpio_to_irq()获取与该GPIO对应的中断号,再通过request_threaded_irq()注册中断回调。按键按下时引脚电平变化触发中断,回调函数中读取GPIO电平状态并打印,同时返回IRQ_HANDLED表示中断已处理。

关键完整代码

// key_interrupt.c
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/of_gpio.h>
#include <linux/of.h>
#include <linux/delay.h>

#define KEY_GPIO 58

static int key_irq;

static irqreturn_t key_isr(int irq, void *dev_id)
{
    int val = gpio_get_value(KEY_GPIO);
    pr_info("Key interrupt triggered! GPIO value = %d\n", val);
    return IRQ_HANDLED;
}

static int key_probe(struct platform_device *pdev)
{
    int ret;

    // 1. 申请GPIO
    ret = gpio_request(KEY_GPIO, "key-gpio");
    if (ret) {
        pr_err("Failed to request GPIO %d\n", KEY_GPIO);
        return ret;
    }

    // 2. 设置为输入方向
    ret = gpio_direction_input(KEY_GPIO);
    if (ret) {
        pr_err("Failed to set GPIO %d as input\n", KEY_GPIO);
        gpio_free(KEY_GPIO);
        return ret;
    }

    // 3. 获取中断号
    key_irq = gpio_to_irq(KEY_GPIO);
    if (key_irq < 0) {
        pr_err("Failed to map GPIO %d to IRQ\n", KEY_GPIO);
        gpio_free(KEY_GPIO);
        return key_irq;
    }

    // 4. 注册中断(按键按下为下降沿触发)
    ret = request_threaded_irq(key_irq, NULL, key_isr,
                               IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                               "key-interrupt", NULL);
    if (ret) {
        pr_err("Failed to request IRQ %d\n", key_irq);
        gpio_free(KEY_GPIO);
        return ret;
    }

    pr_info("Key driver probed, IRQ = %d\n", key_irq);
    return 0;
}

static int key_remove(struct platform_device *pdev)
{
    free_irq(key_irq, NULL);
    gpio_free(KEY_GPIO);
    pr_info("Key driver removed\n");
    return 0;
}

static const struct of_device_id key_of_match[] = {
    { .compatible = "qcom,key-gpio" },
    { }
};
MODULE_DEVICE_TABLE(of, key_of_match);

static struct platform_driver key_driver = {
    .probe  = key_probe,
    .remove = key_remove,
    .driver = {
        .name = "key_interrupt",
        .of_match_table = key_of_match,
    },
};

module_platform_driver(key_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SA8155 GPIO Key Interrupt Driver");

设备树配置

// 在设备树文件中添加节点
key_gpio_node {
    compatible = "qcom,key-gpio";
    key-gpio = <&tlmm 58 0x0>;  // GPIO 58,无特殊配置
    status = "okay";
};

编译与加载

# 编译(内核源码树内)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- M=$(pwd) modules

# 加载模块
adb push key_interrupt.ko /data/local/tmp/
adb shell insmod /data/local/tmp/key_interrupt.ko

# 查看内核日志
adb shell dmesg | grep "Key driver"

验证方法

  1. 加载模块后,执行dmesg | grep "Key driver",应看到Key driver probed, IRQ = xx,说明GPIO申请、中断号获取和中断注册均成功。
  2. 按下按键(GPIO_58被拉低),执行dmesg | tail -5,应看到Key interrupt triggered! GPIO value = 0,说明中断回调被正确触发。
  3. 松开按键后再次按下,应再次看到相同日志,说明中断支持重复触发。
  4. 卸载模块:adb shell rmmod key_interrupt,执行dmesg | grep "Key driver"应看到Key driver removed,且再次按键不再产生中断日志。

通过条件:上述4个步骤的日志输出与预期一致,且卸载后中断不再触发,即判定本案例验证通过。