13 linux内核里实现中断底半部处理的新方法

时间:2021-02-11 17:54:48

原实现底半部的方法参考:http://blog.csdn.net/jklinux/article/details/73550804

现内核提供了新的中断请求函数,这个中断请求函数用于指定中断号为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);

参数irq指定中断号. handler用于指定中断的顶半部处理函数地址, 如果为NULL而thread_fn不为NULL, 则系统会安排一个默认的处理函数. thread_fn用于指定在线程里中断底半部处理函数地址.

如果handler和thread_fn都分别指定了函数地址,则handler指向的处理函数的返回值应为IRQ_WAKE_THREAD, 它可以在顶半部处理函数完成后唤醒底半部线程处理thread_fn指向的函数.
当handler为NULL而thread_fn不为NULL时, irqflags标志需要包含有IRQF_ONESHOT(不可嵌套执行底半部的中断处理函数).

devname与创建出来的底半部线程名有关,线程名为:"irq/中断号-devname".

dev_id用于提供handler和thread_fn指向的函数的(void *)参数.

请求中断成功返回0, 失败会返回相应的错误码.

如实现一个按键中断的中断底半部处理:

/* mydrv.c */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/delay.h>

irqreturn_t irqbh_func(int irqno, void *arg)
{
msleep(1000);
printk("irqbh irqbh\n");
return IRQ_HANDLED;
}

int myprobe(struct platform_device *pdev)
{
struct gpio_desc *gpiod = devm_gpiod_get(&pdev->dev, "btn", GPIOD_IN);
int ret;

if (IS_ERR(gpiod))
{
printk("gpiod get failed\n");
return -ENODEV;
}

ret = devm_request_threaded_irq(&pdev->dev, gpiod_to_irq(gpiod), NULL, irqbh_func, IRQF_TRIGGER_FALLING|IRQF_ONESHOT, pdev->name, gpiod);
if (ret < 0)
goto err0;

platform_set_drvdata(pdev, gpiod);
printk("probe done\n");
return 0;
err0:
devm_gpiod_put(&pdev->dev, gpiod);
return ret;

}

int myremove(struct platform_device *pdev)
{
struct gpio_desc *gpiod = platform_get_drvdata(pdev);

devm_free_irq(&pdev->dev, gpiod_to_irq(gpiod), gpiod);
devm_gpiod_put(&pdev->dev, gpiod);
printk("in myremove\n");
return 0;
}

struct of_device_id ids[] = {
{.compatible = "mykeys"},
{},
};

struct platform_driver mydrv = {
.probe = myprobe,
.remove = myremove,

.driver = {
.owner = THIS_MODULE,
.name = "mydrv" ,

.of_match_table = ids,
},
};

module_platform_driver(mydrv);
MODULE_LICENSE("GPL");