注册中断_中断处理函数_释放中断

1. request_irq() --- 注册中断处理函数:
int request_irq(unsigned int irq, irq_handler_t handler,
                unsigned long irqflags, const char *devname, void *dev_id);
/**
 *    request_irq - allocate an interrupt line
 *    @irq: Interrupt line to allocate
 *    @handler: Function to be called when the IRQ occurs
 *    @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.
 *
 *    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.
 *
 *    Flags:
 *
 *    IRQF_SHARED        Interrupt is shared
 *    IRQF_DISABLED    Disable local interrupts while processing
 *    IRQF_SAMPLE_RANDOM    The interrupt can be used for entropy
 *
 */
int request_irq(unsigned int irq, irq_handler_t handler,
        unsigned long irqflags, const char *devname, void *dev_id)
{
    struct irqaction *action;
    int retval;
#ifdef CONFIG_LOCKDEP
    /*
     * Lockdep wants atomic interrupt handlers:
     */
    irqflags |= IRQF_DISABLED;
#endif
    /*
     * 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).
     */
    if ((irqflags & IRQF_SHARED) && !dev_id)
        return -EINVAL;
    if (irq >= NR_IRQS)
        return -EINVAL;
    if (irq_desc[irq].status & IRQ_NOREQUEST)
        return -EINVAL;
    if (!handler)
        return -EINVAL;
    action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
    if (!action)
        return -ENOMEM;
    action->handler = handler;
    action->flags = irqflags;
    cpus_clear(action->mask);
    action->name = devname;
    action->next = NULL;
    action->dev_id = dev_id;
    select_smp_affinity(irq);
#ifdef CONFIG_DEBUG_SHIRQ
    if (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 do this before actually registering it, to make sure that
         * a 'real' IRQ doesn't run in parallel with our fake
         */
        unsigned long flags;
        local_irq_save(flags);
        handler(irq, dev_id);
        local_irq_restore(flags);
    }
#endif
    retval = setup_irq(irq, action);
    if (retval)
        kfree(action);
    return retval;
}
EXPORT_SYMBOL(request_irq);
参数:
unsigned int irq --- 要分配的中断号。
对某些设备,如传统PC上的系统时钟或键盘,irq值是预先确定的。
对大多数设备来说,这个irq值是可以通过探测获取,或者通过编程动态确定的。
irq_handler_t handler --- 这是一个指针,指向指定的中断处理函数。
只要系统一接收到中断,该函数就被调用。 以后具体讨论。
unsigned long irqflags --- 中断标志位。
可以是0,也可以是一个或多个标志的位掩码。 定义在<linux/interrupt.h> 中
/*
 * These flags used only by the kernel as part of the
 * irq handling routines.
 *
 * IRQF_DISABLED - keep irqs disabled when calling the action handler.
 *                 DEPRECATED. This flag is a NOOP and scheduled to be removed
 * IRQF_SAMPLE_RANDOM - irq is used to feed the random generator
 * 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
 *
 */
#define IRQF_DISABLED		0x00000020    //在处理本中断期间,要禁止所有其他中断。
#define IRQF_SAMPLE_RANDOM	0x00000040    
#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_TIMER		(__IRQF_TIMER | IRQF_NO_SUSPEND)  //为定时器的中断处理指定的标志。
/*
 * These correspond to the IORESOURCE_IRQ_* defines in
 * linux/ioport.h to select the interrupt line behaviour.  When
 * requesting an interrupt without specifying a IRQF_TRIGGER, the
 * setting should be assumed to be "as already configured", which
 * may be as per machine or firmware initialisation.
 */
#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
const char *name --- 指定该中断的设备名。
这个设备名会被 /proc/interrupts 和 /proc/irq 文件引用。
void *dev --- 用于共享中断线。
在释放中断时,dev将提供为唯一的信息标志(cookie), 以便从共享中断线的诸多中断处理程序中删除指定的那个。
如果没有这个参数,内核就不可能知道在给定的中断线上到底要删除哪个。
如果无须共享中断线,可以将该值赋空值(NULL),但是如果中断线是共享的,就必须传递唯一的信息。
返回值:
irq_request( )执行成功,会返回0 ,失败返回非0.
最常见的错误时-EBUSY, 表示给定的中断线已经在使用。
注意:
request_irq()可能会睡眠,因此,不能在中断上下文或其他不允许阻塞的代码中调用该函数。
造成request_irq()可能会睡眠的原因:
在注册中断过程中,内核会在 /proc/irq 文件中创建一个与中断对应的项。函数 proc_mkdir()就是用来创建这个新 procfs 项的, proc_mkdir()通过调用 proc_create(),对这个新的procfs 项进行配置,而 proc_create() 会调用 kmalloc() 来请求分配内存。 由于kmalloc() 是可以睡眠的,所以这就是 request_irq()可能会睡眠的原因。

中断例子:
在一个驱动程序中请求一个中断,并通过request_irq()安装中断处理函数:
if(request_irq(irqn, my_interrupt, IRQF_SHARED, "my_device", my_dev)){
        printk(KERN_ERR "my_device: cannot register IRQ %d\n", irqn);
        return -EIO;
}
irqn --- 请求的中断号。
my_interrupt --- 中断处理函数
IRQF_SHARED --- 中断号可以共享
“my_device” --- 设备名
my_dev --- dev 的形参

2. 释放中断:   void free_irq(unsigned int irq, void *dev_id);
卸载驱动程序时,需要注销相应的中断处理程序,并释放中断号。
如果指定的中断号 不是共享的,那么,该函数删除中断处理程序的同时,将禁用这条中断线。
如果中断号是共享的,则仅删除dev所对应的中断处理函数,而该中断号,只有在删除了最后一个中断处理函数之后才会被禁用。
所以可以看出, dev 就是用来区分共享同一中断号上面的多个中断处理函数,并让free_irq() 仅仅删除指定的中断处理函数。
 
    /**
     *    free_irq - free an interrupt
     *    @irq: Interrupt line to free
     *    @dev_id: Device identity to free
     *
     *    Remove an interrupt handler. The handler is removed and if the
     *    interrupt line is no longer in use by any driver it is disabled.
     *    On a shared IRQ the caller must ensure the interrupt is disabled
     *    on the card it drives before calling this function. The function
     *    does not return until any executing interrupts for this IRQ
     *    have completed.
     *
     *    This function must not be called from interrupt context.
     */
    void free_irq(unsigned int irq, void *dev_id)
    {
        struct irq_desc *desc;
        struct irqaction **p;
        unsigned long flags;
        WARN_ON(in_interrupt());
        if (irq >= NR_IRQS)
            return;
        desc = irq_desc + irq;
        spin_lock_irqsave(&desc->lock, flags);
        p = &desc->action;
        for (;;) {
            struct irqaction *action = *p;
            if (action) {
                struct irqaction **pp = p;
                p = &action->next;
                if (action->dev_id != dev_id)
                    continue;
                /* Found it - now remove it from the list of entries */
                *pp = action->next;
                /* Currently used only by UML, might disappear one day.*/
    #ifdef CONFIG_IRQ_RELEASE_METHOD
                if (desc->chip->release)
                    desc->chip->release(irq, dev_id);
    #endif
                if (!desc->action) {
                    desc->status |= IRQ_DISABLED;
                    if (desc->chip->shutdown)
                        desc->chip->shutdown(irq);
                    else
                        desc->chip->disable(irq);
                }
                spin_unlock_irqrestore(&desc->lock, flags);
                unregister_handler_proc(irq, action);
                /* Make sure it's not being used on another CPU */
                synchronize_irq(irq);
    #ifdef CONFIG_DEBUG_SHIRQ
                /*
                 * It's a shared IRQ -- the driver ought to be
                 * prepared for it to happen even now it's
                 * being freed, so let's make sure.... We do
                 * this after actually deregistering it, to
                 * make sure that a 'real' IRQ doesn't run in
                 * parallel with our fake
                 */
                if (action->flags & IRQF_SHARED) {
                    local_irq_save(flags);
                    action->handler(irq, dev_id);
                    local_irq_restore(flags);
                }
    #endif
                kfree(action);
                return;
            }
            printk(KERN_ERR "Trying to free already-free IRQ %d\n", irq);
            spin_unlock_irqrestore(&desc->lock, flags);
            return;
        }
    }
 
3. irqreturn_t:中断处理程序只能使用两种返回值:
a. 如果正确处理了IRQ, 则返回IRQ_HANDLED.
b. 如果ISR不负责该IRQ, 则返回IRQ_NONE.
    /*
     * For 2.4.x compatibility, 2.4.x can use
     *
     *    typedef void irqreturn_t;
     *    #define IRQ_NONE
     *    #define IRQ_HANDLED
     *    #define IRQ_RETVAL(x)
     *
     * To mix old-style and new-style irq handler returns.
     *
     * IRQ_NONE means we didn't handle it.
     * IRQ_HANDLED means that we did have a valid interrupt and handled it.
     * IRQ_RETVAL(x) selects on the two depending on x being non-zero (for handled)
     */
    typedef int irqreturn_t;
    #define IRQ_NONE    (0)
    #define IRQ_HANDLED    (1)
    #define IRQ_RETVAL(x)    ((x) != 0)
    #endif


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值