我是靠谱客的博主 冷静小天鹅,这篇文章主要介绍Linux中断处理API介绍,现在分享给大家,希望可以做个参考。

Linux中断处理API介绍

一、注册中断

  Linux内核提供注册中断的方法有requese_irq和request_threaded_irq两个函数。

 1.1、request_threaded_irq函数内核源码分析

/**
 *	request_threaded_irq - allocate an interrupt line
 *	@irq: Interrupt line to allocate//分配的中断号
 *	@handler: Function to be called when the IRQ occurs.//中断处理函数
 *		  Primary handler for threaded interrupts
 *		  If NULL and thread_fn != NULL the default
 *		  primary handler is installed
 *	@thread_fn: Function called from the irq handler thread
 *		    If NULL, no irq thread is created
 *      	//中断线程处理函数,如果为空则不创建中断线程
 *	@irqflags: Interrupt type flags//中断类型标志
 *	@devname: An ascii name for the claiming device//设备的名称
 *	@dev_id: A cookie passed back to the handler function//传递给中断处理函数的参数
 *
 *	This call allocates interrupt resources and enables the
 *	interrupt line and IRQ handling. From the point this
 *	call is made your handler function may be invoked. Since
 *	your handler function must clear any interrupt the board
 *	raises, you must take care both to initialise your hardware
 *	and to set up the interrupt handler in the right order.
 *
 *	If you want to set up a threaded irq handler for your device
 *	then you need to supply @handler and @thread_fn. @handler is
 *	still called in hard interrupt context and has to check
 *	whether the interrupt originates from the device. If yes it
 *	needs to disable the interrupt on the device and return
 *	IRQ_WAKE_THREAD which will wake up the handler thread and run
 *	@thread_fn. This split handler design is necessary to support
 *	shared interrupts.
 *  如果您想为您的设备设置一个线程的irq处理程序,那么您需要提供@handler和
 *  @thread_fn。@handler仍然在硬中断上下文中调用,必须检查中断是否来自设备。
 *  如果是,它需要禁用设备上的中断并返回IRQ_WAKE_THREAD,它将唤醒处理程序
 *  线程并运行@thread_fn。这种分割处理程序设计对于支持共享中断是必要的。
 *	Dev_id must be globally unique. Normally the address of the
 *	device data structure is used as the cookie. Since the handler
 *	receives this value it makes sense to use it.
 *
 *	If your interrupt is shared you must pass a non NULL dev_id
 *	as this is required when freeing the interrupt.
 *  如果您的中断是共享的,那么您必须传递一个非空的dev_id,因为在释放中断时
 *  需要传递一个非空的dev_id。
 *	Flags:
 *
 *	IRQF_SHARED		    Interrupt is shared//共享中断
 *	IRQF_TRIGGER_*		Specify active edge(s) or level//指定边缘触发或水平触发
 *
 */
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)
{
	struct irqaction *action;
	struct irq_desc *desc;
	int retval;
	if (irq == IRQ_NOTCONNECTED)
		return -ENOTCONN;
	/*
	 * Sanity-check: shared interrupts must pass in a real dev-ID,
	 * otherwise we'll have trouble later trying to figure out
	 * which interrupt is which (messes up the interrupt freeing
	 * logic etc).
	 *
	 * Also IRQF_COND_SUSPEND only makes sense for shared interrupts and
	 * it cannot be set along with IRQF_NO_SUSPEND.
	 */
	if (((irqflags & IRQF_SHARED) && !dev_id) ||
	    (!(irqflags & IRQF_SHARED) && (irqflags & IRQF_COND_SUSPEND)) ||
	    ((irqflags & IRQF_NO_SUSPEND) && (irqflags & IRQF_COND_SUSPEND)))
		return -EINVAL;
	desc = irq_to_desc(irq);
	if (!desc)
		return -EINVAL;
	if (!irq_settings_can_request(desc) ||
	    WARN_ON(irq_settings_is_per_cpu_devid(desc)))
		return -EINVAL;
	if (!handler) {
		if (!thread_fn)
			return -EINVAL;
		handler = irq_default_primary_handler;
	}
	action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
	if (!action)
		return -ENOMEM;
	action->handler = handler;
	action->thread_fn = thread_fn;
	action->flags = irqflags;
	action->name = devname;
	action->dev_id = dev_id;
	retval = irq_chip_pm_get(&desc->irq_data);
	if (retval < 0) {
		kfree(action);
		return retval;
	}
	retval = __setup_irq(irq, desc, action);//在__setup_irq中创建中断线程处理函数
	if (retval) {
		irq_chip_pm_put(&desc->irq_data);
		kfree(action->secondary);
		kfree(action);
	}
#ifdef CONFIG_DEBUG_SHIRQ_FIXME
	if (!retval && (irqflags & IRQF_SHARED)) {
		/*
		 * It's a shared IRQ -- the driver ought to be prepared for it
		 * to happen immediately, so let's make sure....
		 * We disable the irq to make sure that a 'real' IRQ doesn't
		 * run in parallel with our fake.
		 */
		unsigned long flags;
		disable_irq(irq);
		local_irq_save(flags);
		handler(irq, dev_id);
		local_irq_restore(flags);
		enable_irq(irq);
	}
#endif
	return retval;
}

  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)

  1、功能:当发生中断号为irq的中断时候,会执行handler所指向的中断处理函数。如果thread_fn不为空,它将唤醒处理程序线程并运行thread_fn。
  2、函数参数
  irq:中断号
  handler:中断处理函数指针,中断发生时最先执行的代码,类似于顶半,最后会return IRQ_WAKE_THREAD来唤醒中断线程thread_fn。
  thread_fn:thread_fn,是要在线程里执行的handler,非常类似于底半。当handler return IRQ_WAKE_THREAD会唤醒中断线程thread_fn。
  devname:设备名称(注册后会出现在cat /proc/interrupts)
  dev_id:传递给中断服务函数的参数。注意:如果为共享中断,那么这个参数一定要有,因为当注销共享中断的中其中一个时,就是通过这个来标识要注销的是哪一个中断。
  3、返回值
  0 表示成功
  -EINVAL(-22)表示中断号无效
  -EBUSY(-16)表示中断号已经被占用

 1.2、request_irq函数内核源码分析

static inline int __must_check  request_irq(unsigned int irq, irq_handler_t handler, 
                               unsigned long flags, const char *name, void *dev)
{
	return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}

  从源码中可以看出,request_irq其实是调用request_threaded_irq来实现的,只不过 irq_handler_t thread_fn=NULL,即不会创建中断线程处理函数,用系统提供的默认函数处理。
 1.3、参数的具体分析

 1.3.1、irq中断号

  中断号一般定义在arch架构(如arm)mach-芯片类型inchudemachIrqs.h中。
  外部中断号的获取一般使用gpio_to_irq来获取。

int gpio_to_irq(unsigned int gpio)

 1.3.2、中断服务函数指针handler、thread_fn

typedef irqreturn_t (*irq_handler_t)(int, void *);
//函数原型
irqreturn_t irq_handler_t(int irq, void *dev_id);

  irq为中断号,dev_id为注册中断时传递给中断服务函数的参数,一般在共享中断时用来区分设备。返回值类型为irqreturn_t,irqreturn_t介绍如下:

enum irqreturn {
	IRQ_NONE		= (0 << 0),//只在共享中断中使用,如果不是本中断则返回这个值
	IRQ_HANDLED		= (1 << 0),//正确执行中断处理函数返回这个值
	IRQ_WAKE_THREAD		= (1 << 1),//表示去唤醒中断处理线程
};
typedef enum irqreturn irqreturn_t;

 1.3.3、flags中断标志

#define IRQF_TRIGGER_NONE	    0x00000000//不设置触发边沿
#define IRQF_TRIGGER_RISING	    0x00000001//设置触发边沿为上升沿
#define IRQF_TRIGGER_FALLING	0x00000002//设置触发边沿为下降沿
#define IRQF_TRIGGER_HIGH   	0x00000004//设置触发方式为高电平
#define IRQF_TRIGGER_LOW	    0x00000008//设置触发方式为低电平
#define IRQF_TRIGGER_MASK	(IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | 
				 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
#define IRQF_TRIGGER_PROBE	    0x00000010
#define IRQF_SHARED	        	0x00000080//共享中断
#define IRQF_ONESHOT		    0x00002000//在hardirq处理程序完成后,不会重新启用中断,保持irq线禁用,而是执行线程中断处理,直到线程处理程序已经运行。

  补充:以上标志只用于外部中断,且可以组合使用,如:irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT;
 1.3.4、devname设备名

  由编程者命名,用来给内核记录这个中断号已经被使用了。

cat /proc/interrupts

 1.3.5、dev_id

  传给中断函数的参数。如果flags指定为共享中断,则不能为空,并且需要保持其唯一性,因为在注销某个中断时,由于中断号都一直,则通过这个参数来区分具体哪一个设备。如果为独享中断,一般会传递一个有意义的指针,方便中断函数中使用。

 1.3.6request_threaded_irq的优点

  在实际应用中,检测耳机的插入一般是通过耳机插孔中机械变化导致一个gpio的电平的变化,在该gpio中断里进行耳机插入处理。但是耳机插入一般都有个抖动的过程,需要消抖处理。最简单的办法是在中断发生后,延时一段时间(例如200ms),然后再检查GPIO状态是否稳定来确定是否有效插入。如果用老的中断方式,就需要借用workqueue等方式,你需要在顶半里激活一个delay 200ms的workqueue,然后在workqueue里检查。用线程化的处理方式,你仅仅需要在thread_fn里sleep 200ms,然后在检查即可。

二、注销中断

const void *free_irq(unsigned int irq, void *dev_id)
//irq:要注销的中断号   dev_id:和注册时写的一样就可以

三、禁止中断

void disable_irq_nosync(unsigned int irq)void disable_irq(unsigned int irq)

  区别:disable_irq_nosync是立刻返回,而disable_irq不会立刻返回,而是等待中断程序执行完成才返回。
  注意:中断服务程序中不能使用disable_irq函数,会导致死锁。

四、使能中断

void enable_irq(unsigned int irq)

  
  
  

最后

以上就是冷静小天鹅最近收集整理的关于Linux中断处理API介绍的全部内容,更多相关Linux中断处理API介绍内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(125)

评论列表共有 0 条评论

立即
投稿
返回
顶部