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"
验证方法
- 加载模块后,执行
dmesg | grep "Key driver",应看到Key driver probed, IRQ = xx,说明GPIO申请、中断号获取和中断注册均成功。 - 按下按键(GPIO_58被拉低),执行
dmesg | tail -5,应看到Key interrupt triggered! GPIO value = 0,说明中断回调被正确触发。 - 松开按键后再次按下,应再次看到相同日志,说明中断支持重复触发。
- 卸载模块:
adb shell rmmod key_interrupt,执行dmesg | grep "Key driver"应看到Key driver removed,且再次按键不再产生中断日志。
通过条件:上述4个步骤的日志输出与预期一致,且卸载后中断不再触发,即判定本案例验证通过。