打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
中断底半部&顶半部tasklet 与 workqueue
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/timer.h>
#include <linux/cdev.h> //与cdev操作相关
#include <linux/sched.h> //与wait等操作相关
#include <linux/interrupt.h> //irq函数操作
#include <linux/device.h> //与cdev操作相关
#include <linux/poll.h>
#include <linux/semaphore.h>

#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/signal.h> //SA_SHIRQ

#include <mach/irqs.h>

#define DEV_NAME "irqs"
dev_t devid;

struct workqueue_struct *myworkq;
static struct delayed_work irq_delay_work;
static struct work_struct irq_work;
static struct tasklet_struct irqtask;
static DEFINE_SEMAPHORE(irq_lock);   //声明信号量,用于锁定设备
static DECLARE_WAIT_QUEUE_HEAD(irq_waitq); //声明等待队列

struct irq_dev {
    struct cdev        *irq_cdev;
    struct class        *irq_class;
};

struct irq_dev mydev;  //这里的结构设计的不好,这样做的意义不明显

static volatile int press[4] = {0,0,0,0}; //按键按下次数统计
static volatile int ev_press = 0;  //按键按下事件标志
static const int  *irqid[4] = {1,2,3,4};//用于申请中断时的dev_id
volatile int *irqid_tmp;

static irqreturn_t irq1(int irq, void *dev_id)
{
    volatile int *irqid_tmp = (volatile int  *)dev_id;
    printk("key1 dev_id:%d\n",*irqid_tmp);
    press[0]++;
    ev_press = 1;
    wake_up_interruptible(&irq_waitq);
    printk("key1 pressed %d times\n",press[0]);
    
    return IRQ_HANDLED;
}

void irq2_tasklet(unsigned long arg)
{
    press[1]++;
    ev_press = 1;
    wake_up_interruptible(&irq_waitq);
    printk("key2 pressed %d times\n",press[1]);
}    

static irqreturn_t irq2(int irq, void *dev_id)
{
    volatile int *irqid_tmp = (volatile int  *)dev_id;
    printk("key2 dev_id:%d\n",*irqid_tmp);
    tasklet_schedule(&irqtask);  //调度tasklet

    return IRQ_HANDLED;
}

void irq3_work(void *arg)
{
    press[2]++;
    ev_press = 1;
    wake_up_interruptible(&irq_waitq);
    printk("key3 pressed %d times\n",press[2]);
}

static irqreturn_t irq3(int irq, void *dev_id)
{
    int ret;

    volatile int *irqid_tmp = (volatile int  *)dev_id;
    printk("key3 dev_id:%d\n",*irqid_tmp);
    ret = schedule_work(&irq_work); //调度工作队列
    if(ret!=1) {
        printk("irq3 work schedule error\n");
        printk("ret=%d\n",ret);
    }

    return IRQ_HANDLED;
}

void irq4_work_delay(void *arg)
{
    press[3]++;
    ev_press = 1;
    wake_up_interruptible(&irq_waitq);
    printk("key4 pressed %d times\n",press[2]);
}

static irqreturn_t irq4(int irq, void *dev_id)
{
    int ret;

    volatile int *irqid_tmp = (volatile int  *)dev_id;
    printk("key4 dev_id:%d\n",*irqid_tmp);
    ret = queue_delayed_work(myworkq,&irq_delay_work,10);
    if(ret!=1) {
        printk("irq4 delayed_work schedule error\n");
        printk("ret=%d\n",ret);
    }

    return IRQ_HANDLED;
}

static int irq_open(struct inode *inode, struct file *file)
{
    int ret[4];

    down(&irq_lock);

    ret[0] = request_irq(IRQ_EINT(0),irq1,IRQF_TRIGGER_FALLING,"irq_1",&irqid[0]);
    ret[1] = request_irq(IRQ_EINT(1),irq2,IRQF_TRIGGER_FALLING,"irq_2",&irqid[1]);
    ret[2] = request_irq(IRQ_EINT(2),irq3,IRQF_TRIGGER_FALLING,"irq_3",&irqid[2]);
    ret[3] = request_irq(IRQ_EINT(3),irq4,IRQF_TRIGGER_FALLING,"irq_4",&irqid[3]);

    if(ret[0]||ret[1]||ret[2]||ret[3]) {
        if(!ret[0]) free_irq(IRQ_EINT(0),&irqid[0]);
        if(!ret[1]) free_irq(IRQ_EINT(1),&irqid[1]);
        if(!ret[2]) free_irq(IRQ_EINT(2),&irqid[2]);
        if(!ret[3]) free_irq(IRQ_EINT(3),&irqid[3]);
        printk("irq request error\n");
    }

    tasklet_init(&irqtask,irq2_tasklet,0);
    
    myworkq = create_workqueue("irqwork");
    INIT_DELAYED_WORK(&irq_delay_work,irq4_work_delay);
    
    INIT_WORK(&irq_work,irq3_work);

    memset(press,0,sizeof(press));

    printk("opened irq \n");

    return 0;
}

static int irq_close(struct inode *inode, struct file *file)
{
    
    free_irq(IRQ_EINT(0),&irqid[0]);
    free_irq(IRQ_EINT(1),&irqid[1]);
    free_irq(IRQ_EINT(2),&irqid[2]);
    free_irq(IRQ_EINT(3),&irqid[3]);
    
    ev_press = 0;

    tasklet_kill(&irqtask);
    if(!cancel_delayed_work(&irq_delay_work)) flush_workqueue(myworkq);
    destroy_workqueue(myworkq);

    memset(press,0,sizeof(press));

    printk("closed\n");
    
    up(&irq_lock);

    return 0;
}

static int irq_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
    int err;

    wait_event_interruptible(irq_waitq,ev_press); //等待按键按下的事件,如果没有则休眠
    ev_press = 0;

        err = copy_to_user(buff,(const void *)press,min(sizeof(press),count));
        if(err) printk("cpy to user err\n");
    
    printk("read\n");
    return 0;
}

static unsigned irq_poll(struct file *file, poll_table *wait)
{
    unsigned int mask=0;
    poll_wait(file,&irq_waitq,wait);  //开始就会进入poll,调入等待队列,但是没有马上进入睡眠,只是加入等待队列
    
    if(ev_press) mask |= POLL_IN | POLLRDNORM;

//    printk("poll\n");
    return mask;
}

static struct file_operations irqop = {
    .owner = THIS_MODULE,
    .open  = irq_open,
    .read  = irq_read,
    .poll  = irq_poll,
    .release = irq_close,
};

static int __init irq_init(void)
{
    int ret;

    ret = alloc_chrdev_region(&devid,0,1,DEV_NAME);
    if(ret==-1) {
        printk("alloc chardev error\n");
        return -1;
    }

    mydev.irq_cdev = cdev_alloc();    
    cdev_init(mydev.irq_cdev,&irqop);
    mydev.irq_cdev->owner = THIS_MODULE;
    mydev.irq_cdev->ops = &irqop;

    ret = cdev_add(mydev.irq_cdev,devid,1);
    if(ret==-1) {
        printk("cdev add error\n");
        return -1;
    }
    
    mydev.irq_class = class_create(THIS_MODULE,DEV_NAME);
    device_create(mydev.irq_class,NULL,devid,NULL,"%s",DEV_NAME);

    printk("initlized\n");
    return 0;
}


static void __exit irqexit(void)
{
         cdev_del(mydev.irq_cdev);
        device_destroy(mydev.irq_class,devid);
        class_destroy(mydev.irq_class);
       unregister_chrdev_region(devid,1);
}


module_init(irq_init);
module_exit(irqexit);

MODULE_LICENSE("GPL");

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
按键中断驱动
【精品博文】嵌入式监控系统喷水模块的设计
手把手带你写一个中断输入设备驱动
Linux字符设备驱动框架(设备号是一个32位的整数用来唯一标识Linux系统中的所有设备)
驱动之字符设备----按键中断
问下关于put_user问题 - Linux/Unix
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服