设备树文件

 key{
			compatible = "alientek,key";
			pinctrl-names = "default";
			pinctrl-0 = <&pinctrl_key>;
			key-gpios = <&gpio1 18 GPIO_ACTIVE_HIGH>;
			//
			interrupt-parent = <&gpio1>;
			interrupts = <18 IRQ_TYPE_EDGE_BOTH>;
			status = "okay";
		};

interrupt-parent :属性值为“gpio1”,因为 KEY0 所使用的 GPIO 为GPIO1_IO18,也就是设置 KEY0 的 GPIO 中断控制器为 gpio1。
interrupts 属性:设置中断源,第一个 cells 的 18 表示 GPIO1 组的 18号 IO。IRQ_TYPE_EDGE_BOTH 定义在文件 include/linux/irq.h 中

中断

/include/linux/interrupt.h

/*
 * These flags used only by the kernel as part of the
 * irq handling routines.
 *
 * IRQF_SHARED - allow sharing the irq among several devices
 * IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
 * IRQF_TIMER - Flag to mark this interrupt as timer interrupt
 * IRQF_PERCPU - Interrupt is per cpu
 * IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing
 * IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is
 *                registered first in an shared interrupt is considered for
 *                performance reasons)
 * IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished.
 *                Used by threaded interrupts which need to keep the
 *                irq line disabled until the threaded handler has been run.
 * IRQF_NO_SUSPEND - Do not disable this IRQ during suspend.  Does not guarantee
 *                   that this interrupt will wake the system from a suspended
 *                   state.  See Documentation/power/suspend-and-interrupts.txt
 * IRQF_FORCE_RESUME - Force enable it on resume even if IRQF_NO_SUSPEND is set
 * IRQF_NO_THREAD - Interrupt cannot be threaded
 * IRQF_EARLY_RESUME - Resume IRQ early during syscore instead of at device
 *                resume time.
 * IRQF_COND_SUSPEND - If the IRQ is shared with a NO_SUSPEND user, execute this
 *                interrupt handler after suspending interrupts. For system
 *                wakeup devices users need to implement wakeup detection in
 *                their interrupt handlers.
 */
#define IRQF_SHARED		0x00000080
#define IRQF_PROBE_SHARED	0x00000100
#define __IRQF_TIMER		0x00000200
#define IRQF_PERCPU		0x00000400
#define IRQF_NOBALANCING	0x00000800
#define IRQF_IRQPOLL		0x00001000
#define IRQF_ONESHOT		0x00002000
#define IRQF_NO_SUSPEND		0x00004000
#define IRQF_FORCE_RESUME	0x00008000
#define IRQF_NO_THREAD		0x00010000
#define IRQF_EARLY_RESUME	0x00020000
#define IRQF_COND_SUSPEND	0x00040000

中断API

获取中断号
unsigned int irq_of_parse_and_map(struct device_node *dev, int index)

dev:设备节点
index:索引号,interrupts 属性可能包含多条中断信息,通过 index 指定要获取的信息。
返回值:中断号。

申请中断

request_irq 函数用于申请中断,request_irq函数可能会导致睡眠,不能在中断上下文或者其他禁止睡眠的代码段中使用 request_irq 函数。request_irq 函数会激活(使能)中断,不需要手动去使能中断

int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)

irq:要申请中断的中断号。
handler:中断处理函数,当中断发生以后就会执行此中断处理函数。
flags:中断标志

标志 描述
IRQF_SHARED 多个设备共享一个中断线,共享的所有中断都必须指定此标志。如果使用共享中断的话,request_irq 函数的 dev 参数就是唯一区分他们的标志。
IRQF_ONESHOT 单次中断,中断执行一次就结束。
IRQF_TRIGGER_NONE 无触发。
IRQF_TRIGGER_RISING 上升沿触发。
IRQF_TRIGGER_FALLING 下降沿触发。
IRQF_TRIGGER_HIGH 高电平触发。
IRQF_TRIGGER_LOW 低电平触发。

name:中断名字,设置以后可以在/proc/interrupts 文件中看到对应的中断名字。
dev:如果将 flags 设置为 IRQF_SHARED 的话,dev 用来区分不同的中断,一般情况下将
dev 设置为设备结构体,dev 会传递给中断处理函数 irq_handler_t 的第二个参数。
返回值:0 中断申请成功,其他负值 中断申请失败,如果返回-EBUSY 的话表示中断已经被申请了。

释放中断

free_irq 函数释放掉相应的中断。如果中断不是共享的,那么 free_irq 会删除中断处理函数并且禁止中断。

void free_irq(unsigned int irq, void *dev)

irq:要释放的中断。
dev:如果中断设置为共享(IRQF_SHARED)的话,此参数用来区分具体的中断。共享中断只有在释放最后中断处理函数的时候才会被禁止掉。
返回值:无。

中断处理函数

使用 request_irq 函数申请中断的时候需要设置中断处理函数,中断处理函数格式如下所示:

irqreturn_t (*irq_handler_t) (int, void *)

第一个参数是要中断处理函数要相应的中断号。第二个参数是一个指向 void 的指针,需要与 request_irq 函数的 dev 参数保持一致。用于区分共享中断的不同设备,dev 也可以指向设备数据结构。
中断处理函数的返回值为 irqreturn_t 类型,irqreturn_t 类型定义

 irqreturn_t 结构
	enum irqreturn {
	IRQ_NONE = (0 << 0),
	IRQ_HANDLED = (1 << 0),
	IRQ_WAKE_THREAD = (1 << 1),
};
typedef enum irqreturn irqreturn_t;

一般中断服务函数返回值使用如下形式:return IRQ_RETVAL(IRQ_HANDLED)

中断使能与禁止函数
void enable_irq(unsigned int irq)
void disable_irq(unsigned int irq)
void disable_irq_nosync(unsigned int irq)
local_irq_enable()
local_irq_disable()
local_irq_save(flags)
local_irq_restore(flags)

中断号

每个中断都有一个中断号,通过中断号即可区分不同的中断

2、上半部下半部

2.1、软中断

  • 软中断不能睡眠:软中断处理函数运行在中断上下文中,不能调用可能睡眠的函数
  • 软中断适合处理高频、轻量级的任务。对于复杂的任务,考虑使用tasklet或工作队列。
  • 优先级:软中断有固定的优先级,数字越小优先级越高
  • 并发性:同一个软中断可以在不同的 CPU 上同时运行,因此必须做好锁保护
  • 使用场景:对性能要求极高的场景,如网络子系统、块设备子系统。一般不推荐驱动程序开发者直接使用。
/* 在include/linux/interrupt.h中 */
enum {
    HI_SOFTIRQ=0,
    TIMER_SOFTIRQ,
    NET_TX_SOFTIRQ,
    NET_RX_SOFTIRQ,
    BLOCK_SOFTIRQ,
    BLOCK_IOPOLL_SOFTIRQ,
    TASKLET_SOFTIRQ,
    SCHED_SOFTIRQ,
    HRTIMER_SOFTIRQ,
    RCU_SOFTIRQ,
    MY_SOFTIRQ,  /* 自定义软中断 */
    NR_SOFTIRQS
};

/*
nr:要开启的软中断,上面中选一个。
action:软中断对应的处理函数。
返回值:没有返回值。
*/
void open_softirq(int nr, void (*action)(struct softirq_action *))

/*
nr:要触发的软中断
返回值:没有返回值
*/
void raise_softirq(unsigned int nr)

/*软中断函数*/
static void my_softirq_handler(struct softirq_action *action)

2.2、tasklet

基于软中断Softirq

/*定义*/
struct tasklet_struct t;

/*
初始化
t:要初始化的 tasklet
func:tasklet 的处理函数。
data:要传递给 func 函数的参数
*/
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data);

/*
宏 DECLARE_TASKLET 来一次性完成 tasklet 的定义和初始化
*/
DECLARE_TASKLET(name, func, data)

/*
是中断处理函数中调用 tasklet_schedule 函数
t:要调度的 tasklet,
*/
void tasklet_schedule(struct tasklet_struct *t)

2.3、共享工作队列

工作队列是另外一种下半部执行方式,工作队列在进程上下文执行,工作队列将要推后的
工作交给一个内核线程去执行,因为工作队列工作在进程上下文,因此工作队列允许睡眠或重
新调度。因此如果你要推后的工作可以睡眠那么就可以选择工作队列,否则的话就只能选择软
中断或 tasklet。

  • 运行在进程上下文,可以休眠。而软中断和 Tasklet 在中断上下文中,不能休眠
  • 可以抢占:受内核调度器管理。
/*定义*/
struct work_struct w;

/*初始化
_work 表示要初始化的工作,_func 是工作对应的处理函数
*/
#define INIT_WORK(_work, _func)

/*
使用 DECLARE_WORK 宏一次性完成工作的创建和初始化*/
#define DECLARE_WORK(n, f)

/*
中断处理函数中调用
work:要调度的工作。
返回值:0 成功,其他值 失败。
*/
bool schedule_work(struct work_struct *work)

2.4、自定义工作队列

自定义工作队列是由驱动程序或子系统自己创建和拥有的工作队列,它有专属的内核线程。

API 接口

#include <linux/workqueue.h>

/* 1. 创建自定义工作队列 */
struct workqueue_struct *my_wq;
my_wq = alloc_workqueue("my_queue_name", flags, max_active);

/* 2. 调度工作项到自定义工作队列 */
int queue_work(struct workqueue_struct *wq, struct work_struct *work);
int queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay);

/* 3. 刷新(等待)工作队列中的所有工作项完成 */
void flush_workqueue(struct workqueue_struct *wq);

/* 4. 销毁自定义工作队列 */
void destroy_workqueue(struct workqueue_struct *wq);

2.5、延迟工作队列

#include <linux/workqueue.h>

/* 定义并初始化一个延迟工作 */
static DECLARE_DELAYED_WORK(my_delayed_work, my_delayed_work_fn);
或者
/* 动态初始化一个延迟工作项 */
struct delayed_work my_dwork;
INIT_DELAYED_WORK(&my_dwork, my_delayed_work_fn);

/* 调度延迟工作项到共享工作队列
dwork:要执行的任务对象(延迟工作)的指针。
delay:延迟时间(单位是内核时钟节拍数 jiffies)
@delay: 延迟的时间,单位是 jiffies(内核时间单位)
 */
static inline bool schedule_delayed_work(struct delayed_work *dwork, unsigned long delay)
static inline bool queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay)

/* 取消已调度的延迟工作项 */
bool cancel_delayed_work(struct delayed_work *dwork);
bool cancel_delayed_work_sync(struct delayed_work *dwork); // 同步等待取消完成,更安全

/* 注意:刷新和销毁队列的API与普通工作队列相同 */
flush_workqueue(struct workqueue_struct *wq);
destroy_workqueue(struct workqueue_struct *wq)

/* 修改一个已经调度但尚未执行的延迟工作的到期时间 */
int mod_delayed_work(struct workqueue_struct *wq,struct delayed_work *dwork,unsigned long delay);

2.4、中断线程化

Primary Handler:一个非常快速的可选函数,在硬中断上下文中运行,用于验证中断源并进行最快速的硬件应答。如果它能够完全处理中断,则返回 IRQ_HANDLED;否则返回 IRQ_WAKE_THREAD 来唤醒线程处理函数。
Thread Handler:在内核线程中运行的主要处理函数,负责完成所有耗时的操作。它可以休眠。

  • 可以调用 msleep(), schedule(), mutex_lock() 等可能休眠的函数
  • 可以执行较长时间的操作而不会影响系统响应性
  • 可以通过 sched_setscheduler() 设置实时优先级
/*
irq中断号
handler:Primary Handler(硬中断处理函数)。如果为 NULL,完全线程化(primary_handler为NULL),它直接返回 IRQ_WAKE_THREAD。
thread_fn:Thread Handler(线程处理函数)。
flags:中断标志。需要添加 IRQF_ONESHOT,确保线程处理函数完成前不会再次触发 Primary Handler。
name:中断名称(显示在 /proc/interrupts)
dev:传递给线程函数的私有数据指针
*/
int request_threaded_irq(unsigned int irq, 
                        irq_handler_t handler,        // Primary Handler
                        irq_handler_t thread_fn,     // Thread Handler
                        unsigned long flags, 
                        const char *name, 
                        void *dev);
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐