rk3288 中断的线程化处理

适用场景

复杂、耗时的事情,尽量使用内核线程来处理。
工作队列用起来挺简单,但是它有一个缺点:工作队列中有多个 work,前一个 work 没处理完会影响后面的 work。
解决方法有很多种,比如干脆自己创建一个内核线程,不跟别的 work 凑在一块了。
在 Linux 系统中,对于存储设备比如 SD/TF 卡,它的驱动程序就是这样做的,它有自己的内核线程。
对于中断处理,还有另一种方法:threaded_irq,线程化的中断处理。

//irq中断号,handler中断上半部处理函数,thread_fn下半部处理函数,flags中断触发条件等位置,中断名称,
extern int __must_check
request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn,
		     unsigned long flags, const char *name, void *dev);

内核机制

调用此函数后,会在struct irq_desc结构体的action里,创建一项链表,其中handler和thread_fn都是创建的时候提供的。
如果你的thread_fn非空,内核会调用khread_create创建中断函数。
当发生中断时内核先调用handler函数,返回IRQ_HANDLED表示中断处理完毕,返回IRQ_WAKE_THREAD则唤醒线程。
如果返回的是IRQ_WAKE_THREAD,内核线程会执行thread_fn,thread_fn执行完后,就可以唤醒等待中断中的线程了。
使用synchronize_irq来等待thread_fn被执行。
request_threaded_irqrequest_threaded_irq函数在kernel\irq\manage.c中

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)
{
	//... 一些标志位设置

	if (!handler) {
		if (!thread_fn)
			return -EINVAL;
		//如果没有设置handler,那么内核默认设置一个handler函数,这个函数直接返回IRQ_WAKE_THREAD
		handler = irq_default_primary_handler;	
	}

	//创建了一个irqaction结构体,并填写了所有数据
	action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
	if (!action)
		return -ENOMEM;

	action->handler = handler;
	action->thread_fn = thread_fn;
	action->flags = irqflags;
	action->name = devname;
	action->dev_id = dev_id;

	retval = irq_chip_pm_get(&desc->irq_data);
	if (retval < 0) {
		kfree(action);
		return retval;
	}

	retval = __setup_irq(irq, desc, action);		//进一步处理

	if (retval) {
		irq_chip_pm_put(&desc->irq_data);
		kfree(action->secondary);
		kfree(action);
	}

	//....
	return retval;
}
EXPORT_SYMBOL(request_threaded_irq);

__setup_irq在kernel\irq\manage.c中

static int
__setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
{
	//...
    if (new->thread_fn && !nested) {
        ret = setup_irq_thread(new, irq, false);			//进一步处理
        if (ret)
            goto out_mput;
        if (new->secondary) {
            ret = setup_irq_thread(new->secondary, irq, true);
            if (ret)
                goto out_thread;
        }
    }
    //...
}

setup_irq_thread在kernel\irq\manage.c中

static int
setup_irq_thread(struct irqaction *new, unsigned int irq, bool secondary)
{
    struct task_struct *t;
    struct sched_param param = {
        .sched_priority = MAX_USER_RT_PRIO/2,
    };

	//中断的内核线程是以irq起始,后面跟终端号,和我们设置的名字
    if (!secondary) {
        t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
                   new->name);
    } else {
        t = kthread_create(irq_thread, new, "irq/%d-s-%s", irq,
                   new->name);
        param.sched_priority -= 1;
    }

    if (IS_ERR(t))
        return PTR_ERR(t);

    sched_setscheduler_nocheck(t, SCHED_FIFO, &param);

    get_task_struct(t);
    new->thread = t;

    set_bit(IRQTF_AFFINITY, &new->thread_flags);
    return 0;
}

我们再分析__handle_irq_event_percpu,在kernel/irq/handle.c中

irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc, unsigned int *flags)
{
    irqreturn_t retval = IRQ_NONE;
    unsigned int irq = desc->irq_data.irq;
    struct irqaction *action;

    record_irq_time(desc);

    for_each_action_of_desc(desc, action) {
        irqreturn_t res;

        trace_irq_handler_entry(irq, action);
        res = action->handler(irq, action->dev_id);				//1.调用注册的handle函数
        trace_irq_handler_exit(irq, action, res);

        if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pF enabled interrupts\n",
                  irq, action->handler))
            local_irq_disable();

        switch (res) {
        case IRQ_WAKE_THREAD:								//2.如果返回的是IRQ_WAKE_THREAD
            /*
             * Catch drivers which return WAKE_THREAD but
             * did not set up a thread function
             */
            if (unlikely(!action->thread_fn)) {
                warn_no_thread(irq, action);
                break;
            }

            __irq_wake_thread(desc, action);				//3.唤醒对应的thread

            /* Fall through to add to randomness */
        case IRQ_HANDLED:
            *flags |= action->flags;
            break;

        default:
            break;
        }

        retval |= res;
    }

    return retval;
}

线程处理函数为irq_thread,代码在kernel/irq/manage.c中

static int irq_thread(void *data)
{
	//...
    while (!irq_wait_for_interrupt(action)) {				//1.休眠等待中断
        irqreturn_t action_ret;

        irq_thread_check_affinity(desc, action);

        action_ret = handler_fn(desc, action);				//2.执行thread_fn
        if (action_ret == IRQ_WAKE_THREAD)
            irq_wake_secondary(desc, action);

        wake_threads_waitq(desc);							//3.唤醒等待thread_fn线程
    }
    //...
}

相关函数

//注册中断线程,irq中断号,handler中断上半部处理函数,thread_fn下半部处理函数,flags中断触发条件等位置,中断名称
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中断号,dev_id中断名称
const void *free_irq(unsigned int irq, void *dev_id)

源码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <linux/irqreturn.h>
#include <linux/gpio/consumer.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/timer.h>
#include <linux/workqueue.h>

struct gpio_key {
        int gpio;
        struct gpio_desc *gpiod;
        int flag;
        int irq;
        struct timer_list timer;
        struct tasklet_struct tasklet;
        struct work_struct work;
};
static struct gpio_key *myBtn_key;
static int button_major = 0;
static struct class *button_class;
static struct fasync_struct *btn_async;

static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);

#define MaxSize 128
struct QNode {
        int Data[MaxSize];
        int rear;
        int front;
};

typedef struct QNode *Queue;

int IsEmpty(Queue Q);
void AddQ(Queue PtrQ, int item);
int DeleteQ(Queue PtrQ);

int IsEmpty(Queue Q)
{
        return (Q->rear == Q->front);   //1:empty 0:not empty
}


void AddQ(Queue PtrQ, int item)
{
    if((PtrQ->rear+1)%MaxSize == PtrQ->front) {
                printk("%s,Queue full\n", __FUNCTION__);
        return;
    }
    PtrQ->rear = (PtrQ->rear+1)%MaxSize;
    PtrQ->Data[PtrQ->rear] = item;
}


int DeleteQ(Queue PtrQ)
{
    if(PtrQ->front == PtrQ->rear) {
                printk("%s,Queue empty\n", __FUNCTION__);
        return -1;
    } else {
        PtrQ->front = (PtrQ->front+1)%MaxSize;
        return PtrQ->Data[PtrQ->front];
    }
}

static Queue irqBuff;
static ssize_t button_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
        int err;
        int val;

        if(IsEmpty(irqBuff) && (file->f_flags & O_NONBLOCK)) {
                return -EAGAIN;
        }

        wait_event_interruptible(gpio_key_wait, !IsEmpty(irqBuff));
        val = DeleteQ(irqBuff);
        err = copy_to_user(buf, &val, 4);
//      if(err != 4) {
//              return -1;
//      }
        return 4;
}

static unsigned int button_poll(struct file *fp, poll_table * wait)
{
        printk("%s,button poll\n", __FUNCTION__);
        poll_wait(fp, &gpio_key_wait, wait);
        return IsEmpty(irqBuff) ? 0 : POLLIN | POLLRDNORM;
}

int button_fasync(int fd, struct file *file, int on)
{
        if(fasync_helper(fd, file, on, &btn_async) >= 0)
                return 0;
        else
                return -EIO;
}

static struct file_operations button_ops = {
        .owner = THIS_MODULE,
        .read  = button_read,
        .poll  = button_poll,
        .fasync = button_fasync,
};

static irqreturn_t myBtn_irq_request(int irq, void *dev_id)
{
        struct gpio_key *gpio_key = dev_id;
        //printk(KERN_WARNING"myBtn_irq_request key %d irq happened\n", gpio_key->gpio);
        tasklet_schedule(&myBtn_key->tasklet);
        schedule_work(&myBtn_key->work);
        mod_timer(&gpio_key->timer, jiffies + HZ/50);
        return IRQ_WAKE_THREAD;
}

static void myBtn_timer(unsigned long data)
{
        struct gpio_key *gpio_key = (struct gpio_key*)data;
        int val;

        val = gpiod_get_value(gpio_key->gpiod);

        printk(KERN_WARNING"key %d %d\n", gpio_key->gpio, val);
        val = (myBtn_key->gpio << 8)|val;
        AddQ(irqBuff, val);
        wake_up_interruptible(&gpio_key_wait);
        kill_fasync(&btn_async, SIGIO, POLLIN);
}

static void myBtn_tasklet(unsigned long data)
{
        struct gpio_key *gpio_key = (struct gpio_key*)data;
        int val;

        val = gpiod_get_value(gpio_key->gpiod);
        printk(KERN_WARNING"tasklet key %d %d\n", gpio_key->gpio, val);
}

static void myBtn_workqueue(struct work_struct *work)
{
        struct gpio_key *gpio_key = container_of(work, struct gpio_key, work);
        int val;

        val = gpiod_get_value(gpio_key->gpiod);
        printk(KERN_WARNING"key_work_func: the process is %s pid %d\n",current->comm, current->pid);
        printk(KERN_WARNING"workqueue key %d %d\n", gpio_key->gpio, val);
}

static irqreturn_t myBtn_irqthreaded(int irq, void *data)
{
        struct gpio_key *gpio_key = data;
        int val;

        val = gpiod_get_value(gpio_key->gpiod);
        printk(KERN_WARNING"myBtn_irq_handler: the process is %s pid %d\n",current->comm, current->pid);
        printk(KERN_WARNING"threaded_irq key %d %d\n", gpio_key->gpio, val);
        return IRQ_HANDLED;
}

static int my_button_probe(struct platform_device *pdev)
{
        struct device_node *node = pdev->dev.of_node;
        int count;
        enum of_gpio_flags flag;
        int i, err;

        count = of_gpio_count(node);
        if(!count) {
                printk("%s,there isn't any gpio availiable\n", __FUNCTION__);
                return -1;
        }

        myBtn_key = (struct gpio_key*)kzalloc(sizeof(struct gpio_key)*count, GFP_KERNEL);
        if(!myBtn_key) {
                printk("%s,kzalloc malloc failed\n", __FUNCTION__);
                return -1;
        }


        for(i=0;i<count;i++) {
                myBtn_key[i].gpio = of_get_gpio_flags(node, i, &flag);
                if(myBtn_key[i].gpio < 0) {
                        printk("%s, of_get_gpio_flags failed\n", __FUNCTION__);
                        return -1;
                }
                myBtn_key[i].gpiod = gpio_to_desc(myBtn_key[i].gpio);
                myBtn_key[i].flag  = flag & OF_GPIO_ACTIVE_LOW;
                myBtn_key[i].irq   = gpio_to_irq(myBtn_key[i].gpio);

                setup_timer(&myBtn_key[i].timer, myBtn_timer, (unsigned long)&myBtn_key[i]);
                myBtn_key[i].timer.expires = ~0;

                tasklet_init(&myBtn_key[i].tasklet, myBtn_tasklet, (unsigned long)&myBtn_key[i]);

                INIT_WORK(&myBtn_key[i].work, myBtn_workqueue);
//              err = request_irq(myBtn_key[i].irq, myBtn_irq_request, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
//                              "myBtn_key", &myBtn_key[i]);
                err = request_threaded_irq(myBtn_key[i].irq, myBtn_irq_request, myBtn_irqthreaded, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
                                "myBtn_key", &myBtn_key[i]);
        }

    button_major = register_chrdev(0, "mybutton", &button_ops);
    if (button_major < 0) {
        printk(KERN_ERR "button : couldn't get a major number.\n");
        return -1;
    }

    button_class = class_create(THIS_MODULE, "button_class");
    if(IS_ERR(button_class)) {
        printk(KERN_ERR "button class: create failed\n");
                unregister_chrdev(button_major, "mybutton");
        return -1;
    }


        device_create(button_class, NULL, MKDEV(button_major, 0), NULL, "mybutton%d", 0);

        return 0;
}

static int my_button_remove(struct platform_device *pdev)
{
        struct device_node *node= pdev->dev.of_node;
        int count;
        int i;

        device_destroy(button_class, MKDEV(button_major, 0));
        class_destroy(button_class);
        unregister_chrdev(button_major, "mybutton");

        count = of_gpio_count(node);
        for(i=0;i<count;i++) {
                free_irq(myBtn_key[i].irq, &myBtn_key[i]);
                del_timer(&myBtn_key[i].timer);
                tasklet_kill(&myBtn_key[i].tasklet);
        }

        kfree(myBtn_key);
        return 0;
}

static struct of_device_id mybuttons[] = {
        { .compatible = "mybtn,btn_drv" },
        { },
};

static struct platform_driver my_button_driver = {
        .probe  = my_button_probe,
        .remove = my_button_remove,
        .driver = {
                .name = "button_dirver",
                .of_match_table = mybuttons,
        },
};

static int gpio_button_init(void)
{
        int err;
        irqBuff = (Queue)kzalloc(sizeof(struct QNode), GFP_KERNEL);
        err = platform_driver_register(&my_button_driver);
        printk(KERN_WARNING"my button dirver init\n");
        return 0;
}

static void gpio_button_exit(void)
{
        platform_driver_unregister(&my_button_driver);
        kfree(irqBuff);
        printk(KERN_WARNING"my button dirver exit\n");
}

module_init(gpio_button_init);
module_exit(gpio_button_exit);
MODULE_LICENSE("GPL");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

习惯就好zz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值