问题描述:
编写一个多级虚拟中断控制器日志
添加打印日志信息
分析步骤
第1步:
第2步:
...
代码片段
设备树
vic: virtual_irq_controller {
compatible = "mycorp,virtual-irq-controller";
interrupt-controller;
// 使用两个 cell: 第一个用于 HWIRQ,第二个用于中断类型
#interrupt-cells = <2>;
// 自定义属性,用于告诉驱动硬件中断号的起始值
mycorp,hwirq-base = <0>;
};
virtual_gpio_controller: vgpio-controller@0 {
compatible = "my-company,virtual-gpio-controller";
gpio-controller;
#gpio-cells = <2>;
ngpios = <32>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&vic>;
interrupts = <10 4>;
};
gpio_irq_tester: gpio-irq-tester {
compatible = "my-company,gpio-irq-tester";
//获取中断号有下面2种方式:
//第一种:下面这种方式使用platform_get_irq方式获取中断号
// interrupt-parent = <&virtual_gpio_controller>;
// interrupts = <5 0>;
//第二种:gpiod_to_irq
red-gpios = <&virtual_gpio_controller 5 0>;
};
一级虚拟控制器代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#define VIC_MAX_IRQS 100 // 支持32个中断
// 私有数据结构
struct vic_chip_data {
struct irq_domain *domain;
u32 hwirq_base;
};
// 中断控制器操作集 (irq_chip)
static void vic_irq_mask(struct irq_data *d) {
pr_info("VIC: Masking public hwirq %lu, irq %u,domain %p\n", d->hwirq, d->irq,d->domain);
}
static void vic_irq_unmask(struct irq_data *d) {
pr_info("VIC: Unmasking public hwirq %lu, irq %u,domain %p\n", d->hwirq, d->irq,d->domain);
}
static int vic_irq_set_type(struct irq_data *d, unsigned int type) {
pr_info("VIC: Setting type %u for hwirq %lu, irq %u,domain %p\n", type, d->hwirq, d->irq,d->domain);
return 0;
}
static struct irq_chip vic_irq_chip = {
.name = "VIC",
.irq_mask = vic_irq_mask,
.irq_unmask = vic_irq_unmask,
.irq_set_type = vic_irq_set_type,
};
// IRQ Domain 的 .map 回调
static int vic_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq) {
// 使用 irq_domain_add_simple 后,这里的 hwirq 就是 public hwirq (e.g., 102)
pr_info("VIC: Mapping public hwirq %lu to Linux irq %u\n", hwirq, irq);
irq_set_chip_and_handler(irq, &vic_irq_chip, handle_level_irq);
irq_set_chip_data(irq, d->host_data);
irq_set_probe(irq);
return 0;
}
static int vic_domain_xlate(struct irq_domain *d, struct device_node *ctrl,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_type)
{
u32 public_hwirq = intspec[0];
//input: intspec[0] = hwirq(硬件中断号), intspec[1] = irq_type(中断类型)
printk("VIC: xlate: intspec[0] %u, intspec[1] %u, intspec[2] %u\n", intspec[0], intspec[1]);
// 核心转换,这里也可以自己进行转换,最终实际的硬件中断号,在proc/interrupts中显示的硬件中断号,在实际触发时也必须是这个
*out_hwirq = public_hwirq;
*out_type = intspec[1];
pr_info("VIC: xlate: public %u -> internal %lu\n", public_hwirq, *out_hwirq);
return 0;
}
// IRQ Domain 操作集
static const struct irq_domain_ops vic_domain_ops = {
.map = vic_irq_map,
// 直接使用内核提供的 twocell 翻译函数,无需自定义
.xlate = irq_domain_xlate_twocell,
// .xlate = vic_domain_xlate,
};
// Sysfs 触发函数
static ssize_t trigger_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) {
struct platform_device *pdev = to_platform_device(dev);
struct vic_chip_data *data = platform_get_drvdata(pdev);
unsigned long public_hwirq;
int linux_irq;
if (kstrtoul(buf, 0, &public_hwirq) < 0) return -EINVAL;
// 检查范围
if (public_hwirq < data->hwirq_base || public_hwirq >= (data->hwirq_base + VIC_MAX_IRQS)) {
dev_err(dev, "Public hwirq %lu is out of range [%u, %u)\n",
public_hwirq, data->hwirq_base, data->hwirq_base + VIC_MAX_IRQS);
return -EINVAL;
}
// 使用 public_hwirq 直接查找映射
linux_irq = irq_find_mapping(data->domain, public_hwirq);
if (linux_irq <= 0) {
dev_err(dev, "Failed to find mapping for public hwirq %lu. Is client driver loaded?\n", public_hwirq);
return -ENXIO;
}
pr_info("VIC: Triggering public hwirq %lu (Linux irq %d)\n", public_hwirq, linux_irq);
// 1. 禁用本地CPU中断,模拟硬件中断的自动行为
local_irq_disable();
generic_handle_irq(linux_irq);
// 3. 重新使能本地CPU中断
local_irq_enable();
return count;
}
static DEVICE_ATTR_WO(trigger);
// 平台驱动 probe 函数
static int vic_probe(struct platform_device *pdev) {
struct device_node *node = pdev->dev.of_node;
struct vic_chip_data *data;
u32 first_irq;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data) return -ENOMEM;
platform_set_drvdata(pdev, data);
if (of_property_read_u32(node, "mycorp,hwirq-base", &first_irq)) {
dev_warn(&pdev->dev, "No 'mycorp,hwirq-base' found, assuming 0\n");
first_irq = 0;
}
data->hwirq_base = first_irq;
// 使用 irq_domain_add_simple 创建域
// data->domain = irq_domain_add_simple(node, VIC_MAX_IRQS, first_irq, &vic_domain_ops, data);
data->domain = irq_domain_add_linear(node, VIC_MAX_IRQS, &vic_domain_ops, data);
printk("VIC: domain %p\n", data->domain);
if (!data->domain) {
dev_err(&pdev->dev, "Failed to create simple IRQ domain\n");
return -ENOMEM;
}
if (device_create_file(&pdev->dev, &dev_attr_trigger)) {
dev_err(&pdev->dev, "Failed to create sysfs 'trigger' file\n");
irq_domain_remove(data->domain);
return -EINVAL;
}
dev_info(&pdev->dev, "Simple Virtual IRQ Controller initialized, range %u-%u\n",
data->hwirq_base, data->hwirq_base + VIC_MAX_IRQS - 1);
return 0;
}
// 平台驱动 remove 函数
static int vic_remove(struct platform_device *pdev) {
struct vic_chip_data *data = platform_get_drvdata(pdev);
device_remove_file(&pdev->dev, &dev_attr_trigger);
irq_domain_remove(data->domain);
pr_info("VIC: Virtual IRQ Controller removed\n");
return 0;
}
static const struct of_device_id vic_of_match[] = {
{ .compatible = "mycorp,virtual-irq-controller" },
{ /* sentinel */ }
};
static struct platform_driver vic_driver = {
.driver = {
.name = "virtual-irq-controller",
.of_match_table = vic_of_match,
},
.probe = vic_probe,
.remove = vic_remove,
};
module_platform_driver(vic_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Simple Virtual Interrupt Controller using irq_domain_add_simple");
二级虚拟控制器代码
/**
* @file 2_vgpio_driver.c
* @brief A complete and final virtual GPIO controller driver, acting as a cascaded IRQ controller.
*
* This version is specifically written to be compatible with older Linux kernels (e.g., v4.4)
* by using the classic method of integrating gpiolib with irqdomain. This involves:
* 1. A custom .to_irq callback on the gpio_chip.
* 2. A custom .map callback on the irq_domain_ops.
* 3. Manual resource management with gpiochip_add/remove and irq_domain_add/remove.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/gpio/driver.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/spinlock.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
/**
* @struct virtual_gpio_chip
* @brief Private data structure for our virtual GPIO controller.
*/
struct virtual_gpio_chip {
/** The core gpio_chip structure provided to gpiolib. */
struct gpio_chip chip;
/** A spinlock to protect shared data from concurrent access. */
spinlock_t lock;
/** Simulates the direction register (0=input, 1=output). */
unsigned int *directions;
/** Simulates the data register (0=low, 1=high). */
unsigned int *values;
/** Pointer to the IRQ domain for managing GPIO-to-IRQ mapping. */
struct irq_domain *domain;
/** The Linux IRQ number of the line connecting to the parent controller. */
int parent_irq;
/** Simulates the interrupt pending/status register. */
unsigned long *irq_pending_mask;
/** Simulates the interrupt enable/mask register. */
unsigned long *irq_enabled_mask;
};
/**
* @brief Helper to get our private data from a generic gpio_chip pointer.
*/
static inline struct virtual_gpio_chip *to_virtual_gpio_chip(struct gpio_chip *chip)
{
return container_of(chip, struct virtual_gpio_chip, chip);
}
//==================================================================================
// 1. GPIO Chip Callback Implementations
//==================================================================================
static int vgpio_request(struct gpio_chip *chip, unsigned int offset)
{
pr_info("vgpio: Requested GPIO %u\n", offset);
return 0;
}
static void vgpio_free(struct gpio_chip *chip, unsigned int offset)
{
pr_info("vgpio: Freed GPIO %u\n", offset);
}
static int vgpio_get_direction(struct gpio_chip *chip, unsigned int offset)
{
return to_virtual_gpio_chip(chip)->directions[offset] == 0;
}
static int vgpio_direction_input(struct gpio_chip *chip, unsigned int offset)
{
to_virtual_gpio_chip(chip)->directions[offset] = 0;
pr_info("vgpio: Set GPIO %u to INPUT\n", offset);
return 0;
}
static int vgpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value)
{
struct virtual_gpio_chip *vgpio = to_virtual_gpio_chip(chip);
vgpio->directions[offset] = 1;
vgpio->values[offset] = !!value;
pr_info("vgpio: Set GPIO %u to OUTPUT, value: %d\n", offset, !!value);
return 0;
}
static int vgpio_get(struct gpio_chip *chip, unsigned int offset)
{
return to_virtual_gpio_chip(chip)->values[offset];
}
static void vgpio_set(struct gpio_chip *chip, unsigned int offset, int value)
{
struct virtual_gpio_chip *vgpio = to_virtual_gpio_chip(chip);
if (vgpio->directions[offset] == 1) {
vgpio->values[offset] = !!value;
}
}
//==================================================================================
// 2. IRQ Chip and Cascaded Handler Implementations
//==================================================================================
static void vgpio_irq_mask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct virtual_gpio_chip *vgpio = to_virtual_gpio_chip(gc);
printk("vgpio: Masking hwirq %lu, irq %u, domain %p\n",d->hwirq, d->irq, d->domain);
// clear_bit(d->hwirq, vgpio->irq_enabled_mask);
}
static void vgpio_irq_unmask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct virtual_gpio_chip *vgpio = to_virtual_gpio_chip(gc);
printk("vgpio: Unmasking hwirq %lu, irq %u, domain %p\n", d->hwirq, d->irq, d->domain);
// set_bit(d->hwirq, vgpio->irq_enabled_mask);
}
static int vgpio_irq_set_type(struct irq_data *d, unsigned int type)
{
// irq_set_handler_locked(d, handle_level_irq);
printk("vgpio: Setting type %u hwirq %lu, irq %u, domain %p\n", type, d->hwirq, d->irq, d->domain);
return 0;
}
static struct irq_chip vgpio_irq_chip = {
.name = "vgpio-irq",
.irq_mask = vgpio_irq_mask,
.irq_unmask = vgpio_irq_unmask,
.irq_set_type = vgpio_irq_set_type,
};
static void vgpio_cascade_irq_handler(struct irq_desc *desc)
{
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
struct virtual_gpio_chip *vgpio = to_virtual_gpio_chip(gc);
struct irq_chip *parent_chip = irq_desc_get_chip(desc);
unsigned long pending;
unsigned int gpio;
printk("vgpio: Cascading IRQ handler\n");
if (parent_chip->irq_mask)
parent_chip->irq_mask(irq_desc_get_irq_data(desc));
pending = *vgpio->irq_pending_mask;
printk("vgpio: Pending IRQs: %x\n",pending);
for_each_set_bit(gpio, &pending, vgpio->chip.ngpio) {
int nested_irq = irq_find_mapping(vgpio->domain, gpio);
if (nested_irq > 0) {
generic_handle_irq(nested_irq);
}
}
*vgpio->irq_pending_mask = 0;
if (parent_chip->irq_unmask)
parent_chip->irq_unmask(irq_desc_get_irq_data(desc));
}
//==================================================================================
// 3. Sysfs Trigger for Debugging
//==================================================================================
static ssize_t trigger_gpio_irq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct virtual_gpio_chip *vgpio = dev_get_drvdata(dev);
unsigned long gpio_offset;
if (kstrtoul(buf, 0, &gpio_offset) < 0 || gpio_offset >= vgpio->chip.ngpio)
return -EINVAL;
printk("vgpio: Triggering IRQ for GPIO %lu\n", gpio_offset);
set_bit(gpio_offset, vgpio->irq_pending_mask);
printk("vgpio: Pending IRQs: %*pb\n", (int)vgpio->chip.ngpio, vgpio->irq_pending_mask);
return count;
}
static DEVICE_ATTR_WO(trigger_gpio_irq);
static struct attribute *vgpio_attrs[] = {
&dev_attr_trigger_gpio_irq.attr,
NULL,
};
static const struct attribute_group vgpio_attr_group = {
.attrs = vgpio_attrs,
};
//==================================================================================
// 4. Kernel 4.4 Compatibility Functions (IRQ Integration)
//==================================================================================
static int vgpio_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq)
{
struct virtual_gpio_chip *vgpio = d->host_data;
printk("vgpio: Mapping IRQ %u hwirq %lu\n", irq, hwirq);
irq_set_chip_and_handler(irq, &vgpio_irq_chip, handle_level_irq);
irq_set_chip_data(irq, &vgpio->chip);
irq_set_probe(irq);
return 0;
}
static int vic_domain_xlate(struct irq_domain *d, struct device_node *ctrl,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_type)
{
const u32 hwirq_base = 0;
u32 public_hwirq = intspec[0];
//input: intspec[0] = hwirq(硬件中断号), intspec[1] = irq_type(中断类型)
printk("VIC: xlate: intspec[0] %u, intspec[1] %u, intspec[2] %u\n", intspec[0], intspec[1]);
// 核心转换,这里也可以自己进行转换,最终实际的硬件中断号,在proc/interrupts中显示的硬件中断号,在实际触发时也必须是这个
*out_hwirq = public_hwirq - hwirq_base;
*out_type = intspec[1];
pr_info("VIC: xlate: public %u -> internal %lu\n", public_hwirq, *out_hwirq);
return 0;
}
static const struct irq_domain_ops vgpio_domain_ops = {
.map = vgpio_irq_map,
// 在此示例中,.xlate 回调并不会调用
// .xlate = irq_domain_xlate_twocell,
// .xlate = vic_domain_xlate,
};
static int vgpio_to_irq(struct gpio_chip *chip, unsigned int offset)
{
int irq;
struct virtual_gpio_chip *vgpio = to_virtual_gpio_chip(chip);
printk("offset = %u\n",offset);
//传入的硬件编号,在这里如果要改变映射关系,可以改变offset的值,offset的值即在设备树中配置的值
irq = irq_create_mapping(vgpio->domain, offset);
printk("irq = %d\n", irq);
return irq;
}
//==================================================================================
// 5. Platform Driver Probe and Remove Functions
//==================================================================================
static int vgpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct virtual_gpio_chip *vgpio;
u32 ngpios;
int ret;
vgpio = devm_kzalloc(dev, sizeof(*vgpio), GFP_KERNEL);
if (!vgpio) return -ENOMEM;
ret = of_property_read_u32(dev->of_node, "ngpios", &ngpios);
if (ret < 0 || ngpios == 0) return -EINVAL;
vgpio->chip.ngpio = ngpios;
vgpio->directions = devm_kcalloc(dev, ngpios, sizeof(*vgpio->directions), GFP_KERNEL);
vgpio->values = devm_kcalloc(dev, ngpios, sizeof(*vgpio->values), GFP_KERNEL);
vgpio->irq_pending_mask = devm_kzalloc(dev, BITS_TO_LONGS(ngpios) * sizeof(long), GFP_KERNEL);
vgpio->irq_enabled_mask = devm_kzalloc(dev, BITS_TO_LONGS(ngpios) * sizeof(long), GFP_KERNEL);
if (!vgpio->directions || !vgpio->values || !vgpio->irq_pending_mask || !vgpio->irq_enabled_mask)
return -ENOMEM;
spin_lock_init(&vgpio->lock);
vgpio->chip.label = dev_name(dev);
vgpio->chip.dev = dev;
vgpio->chip.owner = THIS_MODULE;
vgpio->chip.request = vgpio_request;
vgpio->chip.free = vgpio_free;
vgpio->chip.get_direction = vgpio_get_direction;
vgpio->chip.direction_input = vgpio_direction_input;
vgpio->chip.direction_output = vgpio_direction_output;
vgpio->chip.get = vgpio_get;
vgpio->chip.set = vgpio_set;
vgpio->chip.base = -1;
// --- K4.4 IRQ Integration ---
vgpio->chip.to_irq = vgpio_to_irq;
vgpio->parent_irq = platform_get_irq(pdev, 0);
if (vgpio->parent_irq < 0) return vgpio->parent_irq;
vgpio->domain = irq_domain_add_linear(dev->of_node, ngpios, &vgpio_domain_ops, vgpio);
if (!vgpio->domain) {
dev_err(dev, "Failed to create IRQ domain\n");
return -ENOMEM;
}
ret = gpiochip_add(&vgpio->chip);
if (ret < 0) {
dev_err(dev, "Failed to add gpiochip: %d\n", ret);
goto err_remove_domain;
}
irq_set_chained_handler_and_data(vgpio->parent_irq, vgpio_cascade_irq_handler, &vgpio->chip);
platform_set_drvdata(pdev, vgpio);
sysfs_create_group(&dev->kobj, &vgpio_attr_group);
dev_info(dev, "Virtual GPIO cascaded IRQ controller initialized (FINAL K4.4 compatible)\n");
return 0;
err_remove_domain:
irq_domain_remove(vgpio->domain);
return ret;
}
static int vgpio_remove(struct platform_device *pdev)
{
struct virtual_gpio_chip *vgpio = platform_get_drvdata(pdev);
sysfs_remove_group(&pdev->dev.kobj, &vgpio_attr_group);
gpiochip_remove(&vgpio->chip);
irq_domain_remove(vgpio->domain);
pr_info("vgpio: Controller unregistered.\n");
return 0;
}
//==================================================================================
// 6. Module Boilerplate
//==================================================================================
static const struct of_device_id vgpio_of_match[] = {
{ .compatible = "my-company,virtual-gpio-controller" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, vgpio_of_match);
static struct platform_driver vgpio_driver = {
.driver = {
.name = "vgpio-driver",
.of_match_table = vgpio_of_match,
},
.probe = vgpio_probe,
.remove = vgpio_remove,
};
module_platform_driver(vgpio_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Demo");
MODULE_DESCRIPTION("Cascaded GPIO/IRQ controller (Final K4.4 version)");
测试程序代码
/*
* @Author: your name
* @Date: 2025-10-11 15:48:01
* @LastEditTime: 2025-10-11 16:30:25
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: \linux-4.4.159\drivers\gpu\drm\test\gpio_irq_tester.c
*/
// 3_gpio_irq_tester.c (gpio_to_irq version)
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/gpio/consumer.h> // **需要包含这个新的头文件**
// 中断服务例程 (ISR) 保持不变
static irqreturn_t gpio_tester_isr(int irq, void *dev_id)
{
pr_info("GPIO_IRQ_TESTER: ===> Interrupt received on IRQ %d! (manual conversion) <===\n", irq);
return IRQ_HANDLED;
}
/**
* @brief 平台驱动的 probe 函数 (gpio_to_irq 版本)
*/
static int gpio_tester_probe(struct platform_device *pdev)
{
struct gpio_desc *gpiod;
int irq;
int ret;
// --- 步骤 1: 获取 GPIO 描述符 ---
// devm_gpiod_get() 会解析设备树中的 'gpios' 属性。
// GPIOD_IN 表示我们希望将这个 GPIO 用作输入。
// 'devm_' 版本会自动在驱动卸载时释放 GPIO。
gpiod = devm_gpiod_get(&pdev->dev, "red", GPIOD_IN);
if (IS_ERR(gpiod)) {
ret = PTR_ERR(gpiod);
// EPROBE_DEFER 表示 GPIO 控制器驱动可能还未加载,我们的驱动应该稍后重试。
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "Failed to get GPIO descriptor: %d\n", ret);
return ret;
}
dev_info(&pdev->dev, "Successfully got GPIO descriptor.\n");
// --- 步骤 2: 将 GPIO 描述符转换为 IRQ 号 ---
// 这就是手动转换的关键!
// gpiod_to_irq() 会在内部调用我们 vgpio_driver 中实现的 to_irq 回调。
irq = gpiod_to_irq(gpiod);
// irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "Failed to translate GPIO to IRQ: %d\n", irq);
// 如果 GPIO 控制器不支持中断,这里会返回错误。
return irq;
}
dev_info(&pdev->dev, "Manually translated GPIO to IRQ %d.\n", irq);
// --- 步骤 3: 请求中断 ---
// 注意:因为我们没有从设备树的 'interrupts' 属性获取中断,
// 内核无法自动推断触发类型。因此,在 request_irq 中我们必须明确指定它。
// IRQF_TRIGGER_FALLING 对应设备树中的 <... 2> (下降沿)。
ret = devm_request_irq(&pdev->dev, irq, gpio_tester_isr,
IRQF_TRIGGER_FALLING, "gpio-tester", pdev);
if (ret) {
dev_err(&pdev->dev, "Failed to request IRQ %d: %d\n", irq, ret);
return ret;
}
dev_info(&pdev->dev, "GPIO IRQ tester (manual) is ready.\n");
return 0;
}
// remove 函数和模块定义部分保持不变
static int gpio_tester_remove(struct platform_device *pdev)
{
pr_info("GPIO_IRQ_TESTER: Client driver (manual) removed.\n");
return 0;
}
static const struct of_device_id gpio_tester_of_match[] = {
{ .compatible = "my-company,gpio-irq-tester" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, gpio_tester_of_match);
static struct platform_driver gpio_tester_driver = {
.driver = {
.name = "gpio-tester-driver",
.of_match_table = gpio_tester_of_match,
},
.probe = gpio_tester_probe,
.remove = gpio_tester_remove,
};
module_platform_driver(gpio_tester_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Demo");
MODULE_DESCRIPTION("Test client for cascaded GPIO IRQ (using gpiod_to_irq)");
日志
/ko # insmod vic_driver.ko
VIC: domain bc003e00
virtual-irq-controller virtual_irq_controller: Simple Virtual IRQ Controller initialized, range 0-99
//可以看到分配的硬件中断号是10,与设备树中配置的相同
/ko # insmod virtual-gpio-controller1.ko
VIC: Mapping public hwirq 10 to Linux irq 46
VIC: Setting type 4 for hwirq 10, irq 46,domain bc003e00
VIC: Unmasking public hwirq 10, irq 46,domain bc003e00
vgpio-driver vgpio-controller@0: Virtual GPIO cascaded IRQ controller initialized (FINAL K4.4 compatible)
//可以看到分配的硬件中断号是5,与设备树中配置的相同
/ko # insmod gpio_irq_tester.ko
con_id = red,gpio_suffixes[i]=gpios
vgpio: Requested GPIO 5
vgpio: Set GPIO 5 to INPUT
gpio-tester-driver gpio-irq-tester: Successfully got GPIO descriptor.
offset = 5
vgpio: Mapping IRQ 47 hwirq 5
irq = 47
gpio-tester-driver gpio-irq-tester: Manually translated GPIO to IRQ 47.
vgpio: Setting type 2 hwirq 5, irq 47, domain bb9c69c0
vgpio: Unmasking hwirq 5, irq 47, domain bb9c69c0
gpio-tester-driver gpio-irq-tester: GPIO IRQ tester (manual) is ready.
//查看中断
/ko # cat /proc/interrupts
CPU0 CPU1
38: 67 0 GIC 37 Level uart-pl011
47: 0 0 vgpio-irq 5 Edge gpio-tester
//先模拟GPIO中有一个中断发生
/ko # echo 5 > /sys/devices/platform/vgpio-controller@0/trigger_gpio_irq
vgpio: Triggering IRQ for GPIO 5
vgpio: Pending IRQs: 00000020
//模拟中断发生
/ko # echo 10 > /sys/devices/platform/virtual_irq_controller/trigger
VIC: Triggering public hwirq 10 (Linux irq 46)
vgpio: Cascading IRQ handler
VIC: Masking public hwirq 10, irq 46,domain bc003e00
vgpio: Pending IRQs: 20
vgpio: Masking hwirq 5, irq 47, domain bb9c69c0
GPIO_IRQ_TESTER: ===> Interrupt received on IRQ 47! (manual conversion) <===
vgpio: Unmasking hwirq 5, irq 47, domain bb9c69c0
VIC: Unmasking public hwirq 10, irq 46,domain bc003e00
图片
结论
输出结论
待查资料问题
- 问题 1:?
- 问题 2:?
参考链接
- 官方文档