OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 6190|回复: 4

static irqreturn_t key0_handler(int irq, void *dev_id) 中dev_id参数传递的问题

[复制链接]

8

主题

21

帖子

0

精华

新手上路

积分
30
金钱
30
注册时间
2017-5-25
在线时间
20 小时
发表于 2021-5-11 15:58:41 | 显示全部楼层 |阅读模式
10金钱
本帖最后由 lvshuanghui 于 2021-5-11 16:00 编辑




/* imx6uirq设备结构体 */
struct imx6uirq_dev{
    dev_t devid;            /* 设备号   */
    struct cdev cdev;       /* cdev     */
    struct class *class;    /* 类        */
    struct device *device;  /* 设备    */
    int major;              /* 主设备号   */
    int minor;              /* 次设备号   */
    struct device_node  *nd; /* 设备节点 */
    struct irq_keydesc irqkey[KEY_NUM];
    struct timer_list timer;

    atomic_t keyvalue;
    atomic_t releasekey;
    struct work_struct work;
};


struct imx6uirq_dev imx6uirq;   /* imx6uirq设备 */


/*按键中断处理函数*/
/* @description : 按键中断服务函数
* @param - irq : 中断号
* @param - dev_id : 设备结构。
*/

static irqreturn_t key0_handler(int irq, void *dev_id)
{
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)dev_id;

    schedule_work(&dev->work);

    return IRQ_HANDLED;
}
问题:key0_handler函数中的参数dev_id为imx6uirq设备的地址,但是这个地址是怎样传递到dev_id中去的呢?是在下面的request_irq函数中最后一个参数传递过去的嘛?

    /*2.按键中断初始化*/
    dev->irqkey[0].handler = key0_handler;
    dev->irqkey[0].value = KEY0VALUE;
    //dev->irqkey[0].tasklet = key_tasklet;
    for(i = 0;i < KEY_NUM; i++){
        ret = request_irq(dev->irqkey[i].irqnum, dev->irqkey[i].handler, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,
                            dev->irqkey[i].name, &imx6uirq);
        if(ret){
            printk("irq %d request failed\r\n", dev->irqkey[i].irqnum);
            goto fail_irq;
        }
        //tasklet_init(&dev->irqkey.tasklet, key_tasklet, (unsigned long)dev);

        INIT_WORK(&dev->work, key_work);
    }


正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165353
金钱
165353
注册时间
2010-12-1
在线时间
2108 小时
发表于 2021-5-12 01:32:52 | 显示全部楼层
回复

使用道具 举报

8

主题

21

帖子

0

精华

新手上路

积分
30
金钱
30
注册时间
2017-5-25
在线时间
20 小时
 楼主| 发表于 2021-5-12 09:24:33 | 显示全部楼层

管理员,您给回复一下答案吧,谢谢
回复

使用道具 举报

8

主题

21

帖子

0

精华

新手上路

积分
30
金钱
30
注册时间
2017-5-25
在线时间
20 小时
 楼主| 发表于 2021-5-12 11:45:25 | 显示全部楼层
接我提出的问题:key0_handler函数中的参数dev_id为imx6uirq设备的地址,但是这个地址是怎样传递到dev_id中去的呢?是在下面的request_irq函数中最后一个参数传递过去的嘛?答案:是
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
            const char *name, void *dev)
我在网上找到答案了!!!
-------------------------------------------------------------转自网络,如有侵权,请坐下谈--------------------------
request_irq的作用是申请使用IRQ并注册中断处理程序。

request_irq()函数的原型如下:

/* kernel/irq/manage.c */
int request_irq(

unsigned int irq,
&#160;&#160;&#160;&#160;irqreturn_t (*handler)(int, void *, struct pt_regs *),
&#160;&#160;&#160;&#160;unsigned long irqflags,
&#160;&#160;&#160; const char *devname,
&#160;&#160;&#160; void *dev_id );

&#160;

我们知道,当使用内核共享中断时,request_irq必须要提供dev_id参数,并且dev_id的值必须唯一。那么这里提供唯一的dev_id值的究竟是做什么用的?

&#160;

起先我以为dev_id的值是提供给kernel进行判断共享中断线上的哪一个设备产生了中断(即哪个irqaction产生中断),然后执行相应的中断处理函数(irqaction->handler)。实际上不是的,我们来看看《Linux Kernel Development – Second Edition》第六章中Shared Handlers这一节,其中有段总结性的文字如下:

&#160;

When the kernel receives an interrupt, it invokes sequentially each registered handler on the line. Therefore, it is important that the handler be capable of distinguishing whether it generated a given interrupt. The handler must quickly exit if its associated device did not generate the interrupt. This requires the hardware device to have a status register (or similar mechanism) that the handler can check. Most hardware does indeed have such a feature.

&#160;

这段话的大概意思是,发生中断时,内核并不判断究竟是共享中断线上的哪个设备产生了中断,它会循环执行所有该中断线上注册的中断处理函数(即irqaction->handler函数)。因此irqaction->handler函数有责任识别出是否是自己的硬件设备产生了中断,然后再执行该中断处理函数。通常是通过读取该硬件设备提供的中断flag标志位进行判断。

&#160;

那既然kernel循环执行该中断线上注册的所有irqaction->handler函数,把识别究竟是哪个硬件设备产生了中断这件事交给中断处理函数本身去做,那request_irq的dev_id参数究竟是做什么用的?

&#160;

我总结了一下,实际上dev_id作用主要有两点:

一.在中断处理程序释放时使用到dev_id:

When your driver unloads, you need to unregister your interrupt handler and potentially disable the interrupt line. To do this, call

void free_irq(unsigned int irq, void *dev_id)

……

The fifth parameter, dev_id, is used primarily for shared interrupt lines. When an interrupt handler is freed (discussed later), dev_id provides a unique cookie to allow the removal of only the desired interrupt handler from the interrupt line. Without this parameter, it would be impossible for the kernel to know which handler to remove on a given interrupt line.

&#160;

这里《LKD2》讲了很清楚,当调用free_irq注销中断处理函数时(通常卸载驱动时其中断处理函数也会被注销掉),因为dev_id是唯一的,所以可以通过它来判断从共享中断线上的多个中断处理程序中删除指定的一个。如果没有这个参数,那么kernel不可能知道给定的中断线上到底要删除哪一个处理程序。

&#160;

下面我们来看一下free_irq的代码:

void free_irq(unsigned int irq, void *dev_id)
{
&#160;&#160;&#160; struct irq_desc *desc;
&#160;&#160;&#160;&#160;struct irqaction **p;
&#160;&#160;&#160;&#160;unsigned long flags;

&#160;&#160;&#160;&#160;WARN_ON(in_interrupt());
&#160;&#160;&#160;&#160;if (irq >= NR_IRQS)
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;return;

&#160;&#160;&#160;&#160;desc = irq_desc + irq;&#160;&#160;&#160;&#160;/* 获取该中断号对应的irq_desc */
&#160;&#160;&#160;&#160;spin_lock_irqsave(&desc->lock, flags);
&#160;&#160;&#160;&#160;p = &desc->action;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;/* 找到该irq的irqaction链表 */
&#160;&#160;&#160;&#160;for (;;) {
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;struct irqaction *action = *p;

&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;if (action) {
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;struct irqaction **pp = p;

&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;p = &action->next;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;if (action->dev_id != dev_id)
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;continue; &#160;&#160;/* 这两句进行循环判断,直到找到相应dev_id设备的irqaction */
&#160;

&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;/* Found it - now remove it from the list of entries */
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;*pp = action->next; &#160;&#160;/* 指向下一个irqaction */

&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;/* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;if (desc->chip->release)
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;desc->chip->release(irq, dev_id);
#endif

&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;if (!desc->action) {
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;desc->status |= IRQ_DISABLED;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;if (desc->chip->shutdown)
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;desc->chip->shutdown(irq);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;else
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;desc->chip->disable(irq);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;}
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;recalculate_desc_flags(desc);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;spin_unlock_irqrestore(&desc->lock, flags);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;unregister_handler_proc(irq, action);

&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;/* Make sure it's not being used on another CPU */
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;synchronize_irq(irq);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;kfree(action);&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;/* 删除该设备(dev_id)的irqaction */
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;return;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;}
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;printk(KERN_ERR "Trying to free already-free IRQ %d\n", irq);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;spin_unlock_irqrestore(&desc->lock, flags);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;return;
&#160;&#160;&#160;&#160;}
}

&#160;
重点来了!!!!
二.将使用该中断处理程序的设备结构体传递给该中断处理程序:


首先看两个基础条件:

1)&#160; 内核中的各个设备结构体肯定是唯一的,因此满足dev_id唯一这个要求

2)&#160; dev_id参数会在发生中断时传递给该中断的服务程序。

典型的中断服务程序定义如下:

&#160;

static irqreturn_t intr_handler(int irq, void *dev_id, struct pt_regs *regs);

&#160;

即request_irq的dev_id参数会传递给该中断服务程序的dev_id。因此也可以将驱动程序的设备结构体通过dev_id传递给中断服务程序。

这样做作用主要有两个:

1)&#160;中断服务程序可能使用到设备结构体中的信息

如s3c2410的iis音频驱动,它是将DMA Channel结构体通过dev_id传递给中断服务程序

/* arch/arm/mach-s3c2410/dma.c */
int s3c2410_dma_request(unsigned int channel, struct s3c2410_dma_client *client,void *dev)
{
&#160;&#160;&#160;&#160; err = request_irq(chan->irq, s3c2410_dma_irq, IRQF_DISABLED, client->name, (void *)chan);
……
}

&#160;

目的是中断处理函数s3c2410_dma_irq内需要用到chan结构体的duf成员和load_state成员等。

使用方法如下:

/* arch/arm/mach-s3c2410/dma.c */
static irqreturn_t
s3c2410_dma_irq(int irq, void *devpw, struct pt_regs *regs)
{
&#160;&#160;&#160;&#160;struct s3c2410_dma_chan *chan = (struct s3c2410_dma_chan *)devpw; /* 这里强制转化成request_irq时传给dev_id的设备类型,此处即s3c2410_dma_chan ,下面便可以使用它*/
&#160;&#160;&#160;&#160;struct s3c2410_dma_buf *buf;
&#160;&#160;&#160;&#160;buf = chan->curr;
&#160;&#160;&#160;&#160;……
}

&#160;

因此,我们可以通过将设备结构传递给request_irq的dev_id参数这种机制,在中断处理函数中使用该设备结构体,这是大部分驱动程序通用的一种手法。

&#160;

2)前面我们讲了若使用共享中断,那么中断处理函数自身需要能识别是否是自己的设备产生了中断。通常这是通过读取该硬件设备提供的中断flag标志位进行判断的。

而往往驱动程序里定义的设备结构体通常包含了该设备的IO地址,加上偏移就可以算出中断状态寄存器及中断flag标志位的IO地址信息。因此将设备结构体通过dev_id传递给设备中断处理程序的另一个作用就是使用共享中断时,可以在中断处理函数内通过读取该设备结构 (dev_id) 中提供的中断Flag标志位地址信息进行判断,是否是该设备产生了中断,然后再进一步判断是否继续往下执行还是跳到下一个irqaction->handler函数再判断执行。当然,如果本来就知道该设备中断状态寄存器的IO地址,也可以选择直接readl该地址信息进行判断。
————————————————
版权声明:本文为CSDN博主「围城&amp;微尘」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_27516841/article/details/108541394
回复

使用道具 举报

2

主题

712

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2178
金钱
2178
注册时间
2018-8-27
在线时间
258 小时
发表于 2021-5-14 09:50:55 | 显示全部楼层
你贴这么多有什么用????看request_irq函数定义不就知道了吗,阿尔法驱动开发指南也提到了啊
森罗万象
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-25 20:34

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表