一、注册中断
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介绍内容请搜索靠谱客的其他文章。
发表评论 取消回复