博客
关于我
6.分析request_irq和free_irq函数如何注册注销中断(详解)
阅读量:434 次
发布时间:2019-03-06

本文共 5537 字,大约阅读时间需要 18 分钟。

request_irq和free_irq的使用分析

1. request_irq的功能分析

request_irq 是内核中用来注册中断服务函数的重要函数。它位于 kernel/irq/management.c 中,函数原型如下:

int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id);

参数说明

  • unsigned int irq:要注册的中断号,例如外部中断0对应的号码是16,定义在 mach/irqs.h 中。
  • irq_handler_t handler:要注册的中断服务函数,即 (irq_desc + irq)->action->handler。
  • unsigned long irqflags:触发中断的参数,例如边沿触发,定义在 linux/interrupt.h 中。
  • *const char devname:中断程序的名字,可以通过 cat /proc/interrupt 查看。
  • *void dev_id:传入中断处理程序的参数。注册共享中断时不能为 NULL,因为卸载时需要这个参数。
  • 1.1 request_irq的代码解析

    request_irq 函数主要完成以下步骤:

  • 分配并初始化一个 irqaction 结构体。
  • 将参数赋给该结构体中的各个成员。
  • 调用 setup_irq 函数,将 action 加入中断描述符的 action 链表中。
  • 代码实现如下:

    int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) {    struct irqaction *action;    if (!action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC)) {        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);    return setup_irq(irq, action);}

    1.2 setup_irq的功能分析

    setup_irq 是 request_irq 的一个辅助函数,主要负责将 action 加入中断描述符的 action 链表中,并初始化中断引脚。

    代码实现如下:

    int setup_irq(unsigned int irq, struct irqaction *new) {    struct irq_desc *desc = irq_desc + irq;    struct irqaction *old = &desc->action;    unsigned int shared = 0;    if (old) {        if (((old->flags & new->flags) & IRQF_SHARED) ||            ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {            shared = 1;        } else {            old_name = old->name;            goto mismatch;        }        if (config_intx) {            do {                old = *p;            } while (old);        }        shared = 1;    }    *p = new;    if (!shared) {        if (desc->chip && desc->chip->set_type) {            desc->chip->set_type(irq, new->flags & IRQF_TRIGGER_MASK);        } else {            printk(KERN_WARNING "No IRQF_TRIGGER set_type function for IRQ %d (%s)\n", irq, desc->chip ? desc->chip->name : "unknown");        }        if (!(desc->status & (IRQ_AUTODETECT | IRQ_WAITING | IRQ_INPROGRESS))) {            desc->depth = 0;            desc->status &= ~IRQ_DISABLED;            if (desc->chip->startup) {                desc->chip->startup(irq);            } else {                desc->chip->enable(irq);            }        } else {            desc->depth = 1;        }    }}

    1.3 中断引脚的初始化

    通过 setup_irq 函数,中断引脚的初始化主要通过 desc->chip->set_type 方法完成。以外部中断0为例,函数 s3c_irqext_type 会根据中断类型设置相应的寄存器模式。

    代码实现如下:

    unsigned int s3c_irqext_type(unsigned int irq, unsigned int type) {    void __iomem *extint_reg;    void __iomem *gpcon_reg;    unsigned long gpcon_offset, extint_offset;    if ((irq >= IRQ_EINT0) && (irq < IRQ_EINT3)) {        gpcon_reg = S3C2410_GPFCON;        extint_reg = S3C24XX_EXTINT0;        gpcon_offset = (irq - IRQ_EINT0) * 2;        extint_offset = (irq - IRQ_EINT0) * 4;    } else if (...) {        // 其他中断类型的处理    }    value = __raw_readl(gpcon_reg);    value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset);    switch (type) {        case IRQT_NOEDGE:            printk(KERN_WARNING "No edge setting!\n");            break;        case IRQT_RISING:            newvalue = S3C2410_EXTINT_RISEEDGE;            break;        case IRQT_FALLING:            newvalue = S3C2410_EXTINT_FALLEDGE;            break;        case IRQT_BOTHEDGE:            newvalue = S3C2410_EXTINT_BOTHEDGE;            break;        case IRQT_LOW:            newvalue = S3C2410_EXTINT_LOWLEV;            break;        case IRQT_HIGH:            newvalue = S3C2410_EXTINT_HILEV;            break;        default:    }    value = __raw_readl(extint_reg);    value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset);    __raw_writel(value, extint_reg);    return 0;}

    2. free_irq的功能分析

    free_irq 是用来卸载中断服务函数的函数,位于同一文件中,函数原型如下:

    void free_irq(unsigned int irq, void *dev_id);

    2.1 free_irq的代码解析

    free_irq 函数主要完成以下步骤:

  • 检查当前中断是否被占用。
  • 找到并释放与 dev_id 对应的中断 action。
  • 调用 chip->release 方法,释放中断资源。
  • 更新中断描述符的状态,并关闭或禁用中断。
  • 代码实现如下:

    void free_irq(unsigned int irq, void *dev_id) {    struct irq_desc *desc = irq_desc + irq;    struct irqaction **p;    unsigned long flags;    WARN_ON(in_interrupt());    if (irq >= NR_IRQS) {        return;    }    spin_lock_irqsave(&desc->lock, flags);    p = &desc->action;    for (;;) {        struct irqaction *action = *p;        if (!action) {            spin_unlock_irqrestore(&desc->lock, flags);            return;        }        if (action->dev_id != dev_id) {            *p = action->next;            continue;        }        *p = action->next;        if (desc->chip->release) {            desc->chip->release(irq, dev_id);        }        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);        synchronize_irq(irq);        if (action->flags & IRQF_SHARED) {            handler = action->handler;        }        kfree(action);        return;    }    printk(KERN_ERR "Trying to free already-free IRQ %d\n", irq);    spin_unlock_irqrestore(&desc->lock, flags);    return;}

    2.2 free_irq的卸载流程

  • 找到当前中断的 action 链表头。
  • 遍历 action 链表,找到与 dev_id 匹配的 action。
  • 释放该 action 对应的中断资源。
  • 更新中断描述符的状态,关闭或禁用中断。
  • 通过以上分析,可以清晰地了解 request_irq 和 free_irq 的工作原理,以及如何在内核中注册和卸载中断服务函数。

    转载地址:http://zqiyz.baihongyu.com/

    你可能感兴趣的文章
    Nginx用户认证
    查看>>
    Nginx的Rewrite正则表达式,匹配非某单词
    查看>>
    Nginx的使用总结(一)
    查看>>
    Nginx的可视化神器nginx-gui的下载配置和使用
    查看>>
    Nginx的是什么?干什么用的?
    查看>>
    Nginx访问控制_登陆权限的控制(http_auth_basic_module)
    查看>>
    nginx负载均衡器处理session共享的几种方法(转)
    查看>>
    nginx负载均衡的5种策略(转载)
    查看>>
    nginx负载均衡的五种算法
    查看>>
    Nginx运维与实战(二)-Https配置
    查看>>
    Nginx配置ssl实现https
    查看>>
    Nginx配置TCP代理指南
    查看>>
    Nginx配置——不记录指定文件类型日志
    查看>>
    Nginx配置代理解决本地html进行ajax请求接口跨域问题
    查看>>
    Nginx配置参数中文说明
    查看>>
    Nginx配置好ssl,但$_SERVER[‘HTTPS‘]取不到值
    查看>>
    Nginx配置如何一键生成
    查看>>
    Nginx配置实例-负载均衡实例:平均访问多台服务器
    查看>>
    NIFI1.21.0通过Postgresql11的CDC逻辑复制槽实现_指定表多表增量同步_增删改数据分发及删除数据实时同步_通过分页解决变更记录过大问题_02----大数据之Nifi工作笔记0054
    查看>>
    NIFI从MySql中增量同步数据_通过Mysql的binlog功能_实时同步mysql数据_配置数据路由_实现数据插入数据到目标数据库_实际操作03---大数据之Nifi工作笔记0042
    查看>>