美文网首页
Linux内核中断之中断申请接口

Linux内核中断之中断申请接口

作者: 小田BSP | 来源:发表于2021-05-10 13:29 被阅读0次

    本文基于RockPI 4A单板Linux4.4内核介绍中断申请的常用接口函数。

    一、request_threaded_irq()

    1、文件

    kernel/irq/manage.c
    

    2、定义

    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)
    {
        ...
        
        ## 此处解释了共享中断和dev_id的关系
        /*
         * 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;
        
        ## 获取irq描述符
        desc = irq_to_desc(irq);
        ...
        action->handler = handler;
        action->thread_fn = thread_fn;
        action->flags = irqflags;
        action->name = devname;
        action->dev_id = dev_id;
        ...
        ## 把描述符加入到IRQ链表,完成中断的动态申请及注册。后续介绍
        retval = __setup_irq(irq, desc, action);
        ...
    }
    

    说明:

    1)、irq:要申请的中断号,可通过platform_get_irq()获取,见“Linux内核中断之获取中断号”。

    2)、handler:中断处理函数,发生中断时,先处理中断处理函数,然后返回IRQ_WAKE_THREAD唤醒中断处理线程。中断处理函数尽可能简单。

    中断处理函数定义:typedef irqreturn_t (*irq_handler_t)(int, void *);

    中断返回值如下:

    /**
     * enum irqreturn
     * @IRQ_NONE        interrupt was not from this device or was not handled
     * @IRQ_HANDLED     interrupt was handled by this device
     * @IRQ_WAKE_THREAD handler requests to wake the handler thread
     */
    enum irqreturn {
        IRQ_NONE        = (0 << 0),  ## 不是该设备中断或中断未处理
        IRQ_HANDLED     = (1 << 0),  ## 中断已处理
        IRQ_WAKE_THREAD = (1 << 1),  ## 唤醒中断处理线程
    };
    

    3)、thread_fn:中断处理线程,该参数可为NULL。类似于中断处理函数的下半部分。

    4)、irqflags:中断类型标志。

    定义文件:include/linux/interrupt.h,内容如下:

    #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_SHARED         0x00000080      ## 多设备共享中断
    ...
    

    5)、devname:中断名称,可使用cat /proc/interrupts命令查看。

    6)、dev_id:设备ID,该值唯一。

    在使用共享中断时(即设置IRQF_SHARED),必须传入dev_id,在中断处理和释放函数中都会使用该参数。

    注:

    1、request_threaded_irq()函数可替代request_irqtaskletworkqueue的方式。

    2、对应的中断释放函数为:void free_irq(unsigned int, void *),需要和中断申请函数成对出现。

    二、request_irq()

    1、文件

    include/linux/interrupt.h
    

    2、定义

    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);
    }
    

    说明:

    1)、__must_check:指调用函数一定要处理函数的返回值,否则编译器会给出警告。

    2)、request_irq()函数本质上是中断处理线程thread_fn为空的request_threaded_irq()函数。

    对应的中断释放函数为:void free_irq(unsigned int, void *),需要和中断申请函数成对出现。

    三、devm_request_threaded_irq()

    1、文件

    kernel/irq/devres.c
    

    2、定义

    int devm_request_threaded_irq(struct device *dev, unsigned int irq,
                      irq_handler_t handler, irq_handler_t thread_fn,
                      unsigned long irqflags, const char *devname,
                      void *dev_id)
    {
        ...
        ## 1.申请设备资源
        dr = devres_alloc(devm_irq_release, sizeof(struct irq_devres),
                  GFP_KERNEL);
        ...
        ## 2.调用request_threaded_irq()函数
        rc = request_threaded_irq(irq, handler, thread_fn, irqflags, devname,
                      dev_id);
        ...
        ## 3.添加到设备资源列表中
        devres_add(dev, dr);
        ...
    }
    

    说明

    devm_request_threaded_irq()本质上还是使用request_threaded_irq()函数实现中断申请。

    两者区别:

    1)多了一个dev参数;

    2)在设备驱动卸载时,中断会自动释放;

    3)如果想单独释放中断,可使用void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id)函数。

    四、devm_request_irq()

    1、文件

    include/linux/interrupt.h
    

    2、定义

    static inline int __must_check
    devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler,
             unsigned long irqflags, const char *devname, void *dev_id)
    {
        return devm_request_threaded_irq(dev, irq, handler, NULL, irqflags,
                         devname, dev_id);
    }
    

    devm_request_irq()函数本质上是中断处理线程thread_fn为空的devm_request_threaded_irq()函数。

    五、代码举例

    1、获取中断号

    static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
                     void *data)
    {
        ...
        ## 从dts中获取中断号,irq返回值为55
        irq = platform_get_irq(pdev, 0);
        if (irq < 0)
            return irq;
        ...
        ret = dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data);
        ...
    }
    

    2、申请中断

    int dw_hdmi_bind(struct device *dev, struct device *master,
             void *data, struct drm_encoder *encoder,
             struct resource *iores, int irq,
             const struct dw_hdmi_plat_data *plat_data)
    {
        ...
        ## 申请中断,使用的是IRQF_SHARED,dev_id必须传入参数
        ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq,
                        dw_hdmi_irq, IRQF_SHARED,
                        dev_name(dev), hdmi);
        ...
    }
    

    3、中断处理函数

    static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id)
    {
        ...
        irqreturn_t ret = IRQ_NONE;  ## 中断返回值
        ...
        intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
        if (intr_stat) {
            hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
            return IRQ_WAKE_THREAD; ## 中断返回值,唤醒中断处理线程
        }
        ...
        return ret;
    }
    

    4、中断处理线程

    static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
    {
        struct dw_hdmi *hdmi = dev_id;
        ...
        hdcp_stat = hdmi_readb(hdmi, HDMI_A_APIINTSTAT);
        if (hdcp_stat) {
            if (hdmi->hdcp && hdmi->hdcp->hdcp_isr)
                hdmi->hdcp->hdcp_isr(hdmi->hdcp, hdcp_stat);
            hdmi_writeb(hdmi, hdcp_stat, HDMI_A_APIINTCLR);
            hdmi_writeb(hdmi, 0x00, HDMI_A_APIINTMSK);
        }
    
        return IRQ_HANDLED; ## 中断已处理
    }
    

    5、查看中断

    root@linaro-alip:~# cat /proc/interrupts
               CPU0       CPU1       CPU2       CPU3       CPU4       CPU5
     14:          0          0          0          0          0          0     GICv3  29 Edge      arch_timer
     15:      14722       1928       1966       1848       3725       4894     GICv3  30 Edge      arch_timer
     ...
     55:          0          0          0          0          0          0     GICv3  55 Level     ff940000.hdmi, dw-hdmi-cec
    ...
    

    相关文章

      网友评论

          本文标题:Linux内核中断之中断申请接口

          本文链接:https://www.haomeiwen.com/subject/edhidltx.html