qemu模拟的一个内核驱动 io口中断

printf "\x01\x00\x00\x00" > /dev/qemu-io-intr

gic_poke_irq: mask=0x40000000, offset=0x200 (hwirq=94)

IO-INTR Interrupt triggered! (irq=27, count=12, cpu=0)

IO-INTR Trigger IRQ 27 pending state

/ #

/ #

/ #

/ # cat /proc/interrupts

CPU0

16: 162498 GIC 29 Edge twd

17: 6 GIC 34 Level timer

27: 12 GIC 94 Level qemu-io-intr

29: 0 GIC 47 Level eth0

32: 1065 GIC 41 Level mmci-pl18x (cmd)

33: 40749 GIC 42 Level mmci-pl18x (pio)

34: 8 GIC 44 Level kmi-pl050

35: 100 GIC 45 Level kmi-pl050

36: 241 GIC 37 Level uart-pl011

42: 0 GIC 36 Level rtc-pl031

IPI0: 0 CPU wakeup interrupts

IPI1: 0 Timer broadcast interrupts

IPI2: 0 Rescheduling interrupts

IPI3: 0 Function call interrupts

IPI4: 0 Single function call interrupts

IPI5: 0 CPU stop interrupts

IPI6: 0 IRQ work interrupts

IPI7: 0 completion interrupts

Err: 0

#if 1

#include <linux/module.h>

#include <linux/platform_device.h>

#include <linux/fs.h>

#include <linux/cdev.h>

#include <linux/irq.h>

#include <linux/interrupt.h>

#include <linux/io.h>

#include <linux/sched.h>

#include <linux/uaccess.h>

#include <linux/irqdesc.h>

#include <linux/of.h>

#include <linux/errno.h>

#include <linux/slab.h>

#include <linux/moduleparam.h>

#define DEV_NAME "qemu-io-intr"

static int dev_major = 241;

#define VIRTUAL_IRQ 27 // 内核虚拟中断号

static int irq_count = 0; // 中断计数

static dev_t devno; // 设备号

static struct cdev cdev; // 字符设备结构体

static struct class *io_intr_class;

static int test_irq_dev_id = 1; // 中断dev_id(必须和request_irq保持一致)

// 中断处理函数(自动ACK模式:仅计数+日志,内核自动处理ACK)

irqreturn_t io_intr_handler(int irq, void *dev_id)

{

// 移除手动清除挂起位的代码,由内核自动ACK

irq_count++;

printk(KERN_INFO "IO-INTR Interrupt triggered! (irq=%d, count=%d, cpu=%d)\n",

irq, irq_count, raw_smp_processor_id());

// 返回IRQ_HANDLED后,内核会自动完成中断应答和清除

return IRQ_HANDLED;

}

// write函数:仅触发中断,不手动处理ACK

ssize_t io_intr_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)

{

u32 val = 0;

int ret = 0;

if (len < sizeof(u32)) {

return -EINVAL;

}

ret = copy_from_user(&val, buf, sizeof(u32));

if (ret) {

return -EFAULT;

}

// 仅触发中断,内核自动处理ACK

if (val == 1) {

irq_set_irqchip_state(VIRTUAL_IRQ, IRQCHIP_STATE_PENDING, true);

printk(KERN_INFO "IO-INTR Trigger IRQ %d pending state\n", VIRTUAL_IRQ);

}

return sizeof(u32);

}

// read函数:读取中断计数

ssize_t io_intr_read(struct file *filp, char __user *buf, size_t len, loff_t *off)

{

u32 val = irq_count;

int ret = 0;

if (len < sizeof(u32)) {

return -EINVAL;

}

ret = copy_to_user(buf, &val, sizeof(u32));

if (ret) {

return -EFAULT;

}

return sizeof(u32);

}

// 字符设备操作集

static const struct file_operations io_intr_fops = {

.owner = THIS_MODULE,

.write = io_intr_write,

.read = io_intr_read,

.llseek = default_llseek,

};

// platform驱动probe函数(核心:恢复内核自动ACK流程)

static int io_intr_probe(struct platform_device *pdev)

{

int ret;

// 申请中断:使用内核默认的处理流程(自动ACK)

// 关键:移除IRQF_TRIGGER_RISING,使用Level触发(匹配GIC默认),不手动切换handler

ret = request_irq(VIRTUAL_IRQ, // 中断号27

io_intr_handler, // 中断处理函数

IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND, // Level高电平触发(内核自动ACK)

DEV_NAME, // 中断名称

&test_irq_dev_id); // dev_id(和free_irq保持一致)

if (ret < 0) {

printk(KERN_ERR "IO-INTR request_irq failed (ret=%d)\n", ret);

return ret;

}

// 移除手动切换handler和clear_flags的代码:使用内核默认handler(自动ACK)

// 移除enable_irq:request_irq后内核自动使能中断,避免不平衡警告

printk(KERN_INFO "IO-INTR Request irq %d success (auto ACK mode)\n", VIRTUAL_IRQ);

// 字符设备注册流程

if (dev_major) {

devno = MKDEV(dev_major, 0);

ret = register_chrdev_region(devno, 1, DEV_NAME);

} else {

ret = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);

dev_major = MAJOR(devno); // 动态分配时更新主设备号

}

if (ret < 0) {

free_irq(VIRTUAL_IRQ, &test_irq_dev_id); // 传匹配的dev_id

return ret;

}

cdev_init(&cdev, &io_intr_fops);

cdev.owner = THIS_MODULE;

ret = cdev_add(&cdev, devno, 1);

if (ret < 0) {

unregister_chrdev_region(devno, 1);

free_irq(VIRTUAL_IRQ, &test_irq_dev_id);

return ret;

}

// 创建设备类和设备节点(自动生成/dev/qemu-io-intr)

io_intr_class = class_create(THIS_MODULE, DEV_NAME);

if (IS_ERR(io_intr_class)) {

ret = PTR_ERR(io_intr_class);

cdev_del(&cdev);

unregister_chrdev_region(devno, 1);

free_irq(VIRTUAL_IRQ, &test_irq_dev_id);

return ret;

}

device_create(io_intr_class, NULL, devno, NULL, DEV_NAME);

printk(KERN_INFO "IO-INTR Probed successfully! (irq=%d, major=%d)\n",

VIRTUAL_IRQ, dev_major);

return 0;

}

// platform驱动remove函数(修正free_irq传参)

static int io_intr_remove(struct platform_device *pdev)

{

// 清理设备节点

device_destroy(io_intr_class, devno);

class_destroy(io_intr_class);

cdev_del(&cdev);

unregister_chrdev_region(devno, 1);

// 关键:free_irq的dev_id必须和request_irq一致(不能传NULL)

free_irq(VIRTUAL_IRQ, &test_irq_dev_id);

printk(KERN_INFO "IO-INTR Removed successfully (irq_count=%d)\n", irq_count);

return 0;

}

// 设备树匹配表

static const struct of_device_id io_intr_of_match\[\] = {

{ .compatible = "qemu,io-intr" },

{ },

};

MODULE_DEVICE_TABLE(of, io_intr_of_match);

// platform驱动结构体

static struct platform_driver io_intr_driver = {

.probe = io_intr_probe,

.remove = io_intr_remove,

.driver = {

.name = DEV_NAME,

.of_match_table = io_intr_of_match,

.owner = THIS_MODULE,

},

};

// 注册platform驱动

module_platform_driver(io_intr_driver);

module_param(dev_major, int, S_IRUGO);

MODULE_PARM_DESC(dev_major, "Major device number for qemu-io-intr (0=dynamic)");

MODULE_LICENSE("GPL");

MODULE_AUTHOR("xiao dou AI");

#endif

相关推荐
Full Stack Developme5 小时前
JVM 与 Linux 交互的核心原理
linux·运维·jvm
HackTwoHub5 小时前
最新Nessus2026.6.8版本主机漏洞扫描/探测工具Windows/Linux
linux·运维·服务器·安全·web安全·网络安全·安全架构
qq_163135755 小时前
Linux 【04-mkdir命令超详细教程】
linux
qq_163135755 小时前
Linux 【08-mv命令超详细教程】
linux
qq_163135758 小时前
Linux 【07-rm命令超详细教程】
linux
zh路西法9 小时前
【SSH 免密登录全流程】Windows Linux 通用方案
linux·windows·ssh
ScilogyHunter9 小时前
GCC完全指南
linux·gcc
CodeStats9 小时前
从 CPU 指令到 JVM 进程:彻底讲透 Java 执行 main 方法时,类加载、主线程、栈帧入栈的完整底层逻辑
java·linux·开发语言
努力努力再努力wz11 小时前
【内存管理与高并发内存池系列】从 mmap 到 malloc:文件映射、匿名映射与 glibc 内存分配机制详解
linux·c语言·数据结构·数据库·c++·qt·链表
Jurio.12 小时前
开源 Codex Sticky:在终端 Codex CLI 长对话中始终固定底部输入框
linux·rust·github·开源软件·codex·codex cli