threaded irq和irqreturn

lethe1203發表於2024-04-01

irqreturn有以下幾種:

 enum irqreturn {
         IRQ_NONE                = (0 << 0),    // 表示中斷處理程式未處理該中斷,或者沒有發生中斷
         IRQ_HANDLED             = (1 << 0),    // 表示中斷已經被處理
         IRQ_WAKE_THREAD         = (1 << 1),    // 表示中斷處理程式通知核心喚醒一箇中斷執行緒來處理中斷
 };
request_threaded_irq 是 Linux 核心中用於註冊中斷處理程式的函式之一。它允許在中斷處理過程中執行較長時間的操作,而不會阻塞其他重要的核心功能。以下是對

request_threaded_irq 函式如下:

int request_threaded_irq(unsigned int irq, irq_handler_t handler,
                         irq_handler_t thread_fn, unsigned long irqflags,
                         const char *devname, void *dev_id);

// 引數和返回值說明
unsigned int irq:要註冊的中斷號。
irq_handler_t handler:中斷服務函式,即中斷被觸發時所呼叫的函式。這個函式應該返回一個 irqreturn_t 型別的值。
irq_handler_t thread_fn:中斷執行緒服務函式,用於執行較長時間的操作。中斷執行緒服務函式在中斷服務函式之後執行。這個函式也應該返回一個 irqreturn_t 型別的值。
unsigned long irqflags:中斷標誌,控制中斷的行為。常用的標誌包括 IRQF_SHARED(允許共享中斷)、IRQF_TRIGGER_*(中斷觸發方式,例如上升沿觸發、下降沿觸發等)等。
const char *devname:裝置名稱,用於標識中斷請求所屬的裝置。
void *dev_id:裝置識別符號,將傳遞給中斷服務函式和中斷執行緒服務函式作為引數。
函式返回值為 0 表示註冊成功,否則表示註冊失敗,返回的值是一個負數,表示出錯原因。

request_threaded_irq 函式的工作流程如下:

  1. 檢查中斷號是否有效,以及指定的中斷服務函式和中斷執行緒服務函式是否為有效函式。
  2. 分配中斷描述符,併為中斷分配中斷處理程式和中斷執行緒處理程式。
  3. 將中斷處理程式註冊到核心,以便在中斷被觸發時呼叫。
  4. 將中斷執行緒處理程式註冊到核心,以便在中斷被觸發時呼叫。
在成功註冊中斷後,中斷被觸發時,首先會呼叫中斷服務函式,然後再呼叫中斷執行緒服務函式。這樣可以確保中斷服務函式儘快返回,而不會阻塞其他重要的核心功能,而長時間的處理操作可以在中斷執行緒服務函式中執行。

request_threaded_irq driver demo:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/kthread.h> // 包含核心執行緒相關標頭檔案

#define DEMO_IRQ_NUMBER 1

// 定義一個結構體,用於傳遞給中斷處理函式和中斷執行緒處理函式
struct demo_data {
    irq_handler_t irq_handler;
    struct task_struct *thread_task;
};

static irqreturn_t demo_interrupt_handler(int irq, void *dev_id)
{
    struct demo_data *data = (struct demo_data *)dev_id;

    // 處理中斷
    printk(KERN_INFO "Demo interrupt handled\n");

    // 如果有中斷執行緒處理函式,則喚醒執行緒
    if (data->thread_task) {
        wake_up_process(data->thread_task);
    }

    // 返回中斷處理結果
    return IRQ_HANDLED;
}

static irqreturn_t demo_threaded_handler(int irq, void *dev_id)
{
    // 處理中斷
    printk(KERN_INFO "Demo threaded interrupt handled\n");

    // 返回中斷處理結果
    return IRQ_HANDLED;
}

static int demo_thread_fn(void *data)
{
    // 執行長時間的操作,例如複雜計算、檔案操作等
    printk(KERN_INFO "Demo threaded handler executing long operation\n");
    msleep(1000); // 模擬長時間操作

    return 0;
}

static int __init demo_init(void)
{
    int ret;
    struct demo_data *data;

    // 分配記憶體並初始化資料結構
    data = kmalloc(sizeof(struct demo_data), GFP_KERNEL);
    if (!data) {
        printk(KERN_ERR "Failed to allocate memory for demo data\n");
        return -ENOMEM;
    }
    data->irq_handler = demo_interrupt_handler;
    data->thread_task = kthread_create(demo_thread_fn, NULL, "demo_thread_task");
    if (IS_ERR(data->thread_task)) {
        kfree(data);
        printk(KERN_ERR "Failed to create demo thread\n");
        return PTR_ERR(data->thread_task);
    }

    // 註冊中斷處理程式和中斷執行緒處理程式
    ret = request_threaded_irq(DEMO_IRQ_NUMBER, demo_interrupt_handler, demo_threaded_handler, IRQF_SHARED, "demo_interrupt_threaded", (void *)data);
    if (ret) {
        printk(KERN_ERR "Failed to register threaded interrupt handler\n");
        kfree(data);
        return ret;
    }

    printk(KERN_INFO "Demo module initialized\n");
    return 0;
}

static void __exit demo_exit(void)
{
    struct demo_data *data = (struct demo_data *)free_irq(DEMO_IRQ_NUMBER, (void *)demo_interrupt_handler);

    // 釋放中斷資源
    if (data) {
        kfree(data);
    }
    printk(KERN_INFO "Demo module exited\n");
}

module_init(demo_init);
module_exit(demo_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("lethe1203");
MODULE_DESCRIPTION("request_threaded_irq demo");

相關文章