以下是一个完整的例子,演示 interrupt-controller
和 interrupt-parent
的使用,包含设备树、驱动和测试代码:
1. 设备树 (DTS) 示例
/ {
// 顶级中断控制器 (GIC)
gic: interrupt-controller@8000000 {
compatible = "arm,gic-v3";
reg = <0x0 0x8000000 0x0 0x10000>;
interrupt-controller;
#interrupt-cells = <3>; // 中断号格式: [SPI/PPI, 中断号, 触发方式]
};
// 子中断控制器 (GPIO控制器)
gpio-ic: interrupt-controller@9000000 {
compatible = "my-company,my-gpio-ic";
reg = <0x0 0x9000000 0x0 0x1000>;
interrupt-controller;
#interrupt-cells = <2>; // 中断号格式: [中断号, 触发方式]
interrupt-parent = <&gic>; // 指向上级控制器GIC
};
// 实际设备 (按键)
button@9010000 {
compatible = "my-company,my-button";
reg = <0x0 0x9010000 0x0 0x100>;
interrupt-parent = <&gpio-ic>; // 指向GPIO中断控制器
interrupts = <0 1>; // 中断号0,触发方式1(假设1=上升沿)
};
};
2. Linux 驱动代码
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
static irqreturn_t button_irq_handler(int irq, void *dev_id) {
printk(KERN_INFO "Button pressed! IRQ %d triggered\n", irq);
return IRQ_HANDLED;
}
static int my_button_probe(struct platform_device *pdev) {
struct device_node *np = pdev->dev.of_node;
int irq_num;
int ret;
// 从设备树获取中断号
irq_num = of_irq_get_byname(np, "button-irq");
if (irq_num < 0) {
dev_err(&pdev->dev, "Failed to get IRQ\n");
return irq_num;
}
// 注册中断处理函数
ret = devm_request_irq(&pdev->dev, irq_num, button_irq_handler,
IRQF_TRIGGER_RISING, "button-irq", NULL);
if (ret) {
dev_err(&pdev->dev, "Failed to request IRQ %d\n", irq_num);
return ret;
}
printk(KERN_INFO "Button driver initialized, IRQ %d\n", irq_num);
return 0;
}
static int my_button_remove(struct platform_device *pdev) {
printk(KERN_INFO "Button driver removed\n");
return 0;
}
static const struct of_device_id my_button_of_match[] = {
{ .compatible = "my-company,my-button" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_button_of_match);
static struct platform_driver my_button_driver = {
.probe = my_button_probe,
.remove = my_button_remove,
.driver = {
.name = "my-button",
.of_match_table = my_button_of_match,
},
};
module_platform_driver(my_button_driver);
MODULE_LICENSE("GPL");
3. 测试代码 (用户空间)
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/poll.h>
int main() {
int fd;
char buf[10];
struct pollfd pfd;
// 打开设备节点(假设设备节点为 /dev/button0)
fd = open("/dev/button0", O_RDONLY);
if (fd < 0) {
perror("Failed to open device");
return 1;
}
// 使用 poll 等待中断事件
pfd.fd = fd;
pfd.events = POLLPRI; // 优先数据可读(中断触发)
printf("Waiting for button press...\n");
while (1) {
if (poll(&pfd, 1, -1) > 0) {
lseek(fd, 0, SEEK_SET); // 重置文件指针
read(fd, buf, sizeof(buf));
printf("Interrupt detected!\n");
}
}
close(fd);
return 0;
}
4. 关键步骤说明
- 设备树解析:
gpio-ic
通过interrupt-parent
指向gic
,形成中断路由链。button
设备通过interrupt-parent
指向gpio-ic
,并指定中断号0
和触发方式1
。
- 驱动逻辑:
of_irq_get_byname
从设备树解析中断号。devm_request_irq
注册中断处理函数,触发方式为IRQF_TRIGGER_RISING
(上升沿触发)。
- 测试流程:
- 用户程序通过
poll
等待中断事件。 - 当按键按下时,驱动打印日志,用户程序检测到事件并输出。
- 用户程序通过
5. 验证方法
-
编译驱动并加载:
make -C /lib/modules/$(uname -r)/build M=$PWD modules insmod my_button.ko
-
编译测试程序:
gcc test_button.c -o test_button
-
运行测试程序:
./test_button
-
按下硬件按键(或模拟中断),观察
dmesg
输出和测试程序响应。
6. 输出示例
[ 5.123456] Button driver initialized, IRQ 42
[ 6.789012] Button pressed! IRQ 42 triggered
此例子完整展示了从硬件定义到驱动实现的完整流程,通过 interrupt-controller
和 interrupt-parent
构建中断路由,并通过用户程序验证中断触发。