zoukankan      html  css  js  c++  java
  • Smart210学习记录-------linux驱动中断

    Linux中断

     
    Linux 的中断处理分为两个半部,顶半部处理紧急的硬件操作,底半部处理不紧急的耗时操
    作。tasklet 和工作队列都是调度中断底半部的良好机制,tasklet 基于软中断实现。内核定时器也
    依靠软中断实现。

    1.申请和释放中断

    申请中断 int request_irq(unsigned int irq, irq_handler_t handler,  unsigned long irqflags, const char *devname, void *dev_id)

    irq 是要申请的硬件中断号。

    handler 是向系统登记的中断处理函数(顶半部),是一个回调函数,中断发生时,系统调用 这个函数,

    dev_id 参数将被传递给它。

    irqflags是中断处理的属性,可以指定中断的触发方式以及处理方式。在触发方式方面,可以是 IRQF_TRIGGER_RISING、IRQF_TRIGGER_FALLING、IRQF_TRIGGER_HIGH、IRQF_TRIGGER_ LOW 等。在处理方式方面,若设置了 IRQF_DISABLED,表明中断处理程序是快速处理程序,快 速处理程序被调用时屏蔽所有中断,慢速处理程序则不会屏蔽其他设备的驱动;若设置了 IRQF_SHARED,则表示多个设备共享中断,

    dev_id 在中断共享时会用到,一般设置为这个设备 的设备结构体或者 NULL。

    request_irq()返回 0 表示成功,返回-EINVAL 表示中断号无效或处理函数指针为 NULL,返 回-EBUSY 表示中断已经被占用且不能共享。

    顶半部 handler 的类型 irq_handler_t 定义为:
    typedef irqreturn_t (*irq_handler_t)(int, void *);
    typedef int irqreturn_t;     

    释放中断            

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

    2.使能和屏蔽中断

    void disable_irq(int irq);

    void disable_irq_nosync(int irq);

    void enable_irq(int irq); disable_irq_nosync()与 disable_irq()的区别在于前者立即返回,而后者等待目前的中断处理完 成。由于 disable_irq()会等待指定的中断被处理完,因此如果在 n 号中断的顶半部调用 disable_irq(n),会引起系统的死锁,这种情况下,只能调用 disable_irq_nosync(n)。

    下列两个函数(或宏,具体实现依赖于 CPU 体系结构)将屏蔽本 CPU 内的所有中断:

    #define local_irq_save(flags) ...

    void local_irq_disable(void);

    与上述两个禁止中断对应的恢复中断的函数(或宏)是:

    #define local_irq_restore(flags) ...

    void local_irq_enable(void);

    3.底半部机制
    Linux 实现底半部的机制主要有 tasklet、工作队列和软中断。


    (1)tasklet


    tasklet 的使用较简单,我们只需要定义 tasklet 及其处理函数并将两者关联,例如:
    void my_tasklet_func(unsigned long); /*定义一个处理函数*/
    DECLARE_TASKLET(my_tasklet, my_tasklet_func, data);
       /*定义一个 tasklet 结构 my_tasklet,与 my_tasklet_func(data)函数相关联 */


    代码 DECLARE_TASKLET(my_tasklet,my_tasklet_func,data)实现了定义名称为 my_tasklet 的
    tasklet 并将其与 my_tasklet_func()这个函数绑定,而传入这个函数的参数为 data。


    在需要调度 tasklet 的时候引用一个 tasklet_schedule()函数就能使系统在适当的时候进行调度
    运行:
    tasklet_schedule(&my_tasklet);

     (2).工作队列 工作队列的使用方法和 tasklet 非常相似,下面的代码用于定义一个工作队列和一个底半部执 行函数:

    struct work_struct my_wq; /*定义一个工作队列*/

    void my_wq_func(unsigned long); /*定义一个处理函数*/

    通过 INIT_WORK()可以初始化这个工作队列并将工作队列与处理函数绑定:

    INIT_WORK(&my_wq, (void (*)(void *)) my_wq_func, NULL);  /*初始化工作队列并将其与处理函数绑定*/  

    与 tasklet_schedule()对应的用于调度工作队列执行的函数为 schedule_work(),如:

    schedule_work(&my_wq);/*调度工作队列执行*/

     (3).软中断
    软中断(softirq)也是一种传统的底半部处理机制,它的执行时机通常是顶半部返回的时候,asklet 是基于软中断实现的,因此也运行于软中断上下文。


    在 Linux 内核中,用 softirq_action 结构体表征一个软中断,这个结构体中包含软中断处理函数指针和传递给该函数的参数。使用 open_softirq()函数可以注册软中断对应的处理函数,而raise_softirq()函数可以触发一个软中断。

    软中断和 tasklet 运行于软中断上下文,仍然属于原子上下文的一种,而工作队列则运行于进
    程上下文。因此,软中断和 tasklet 处理函数中不能睡眠,而工作队列处理函数中允许睡眠。

    按键中断驱动程序:

    四个按键(对应的IO口为GPH2(0),GPH2(1),GPH2(2),GPH2(3))) 分别对应外部中断为EINT16,17,18,19

    加入了poll机制和异步通知fasync

    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    #include <linux/poll.h>
    #include <linux/irq.h>
    #include <asm/irq.h>
    #include <linux/wait.h>
    #include <linux/sched.h>
    #include <linux/interrupt.h>
    #include <asm/uaccess.h>
    #include <linux/gpio.h>
    #include <mach/regs-gpio.h>
    #include <mach/hardware.h>
    #include <linux/platform_device.h>
    #include <linux/cdev.h>
    #include <linux/miscdevice.h>
    
    #define KEYNAME   "mykey"
    static unsigned char  keymajor = 0;
    
    static struct cdev key_cdev;
    
    static struct class *key_class;
    static struct device*key_device;
    static struct fasync_struct *fasync_queue;
    
    
    static DECLARE_WAIT_QUEUE_HEAD(my_key_waitq); /*DECLARE_WAIT_QUEUE_HEAD (name)声明且初始化*/
    static unsigned char pressed = 0;
    
    static unsigned char key_value = 0;
    
    struct my_key_desc {
        unsigned int  irq;
        unsigned int  pin;
        unsigned char value;
        const char*   name;
    };
    
    static struct my_key_desc  key_desc[] = {
        {IRQ_EINT16_31, S5PV210_GPH2(0), 0x01, "KEY1"},
        {IRQ_EINT16_31, S5PV210_GPH2(1), 0x02, "KEY2"},    
        {IRQ_EINT16_31, S5PV210_GPH2(2), 0x03, "KEY3"},
        {IRQ_EINT16_31, S5PV210_GPH2(3), 0x04, "KEY4"},
    };
    
    
    static irqreturn_t my_key_irq(int irq, void* dev_id)
    {
        volatile struct my_key_desc key_dr = *(volatile struct my_key_desc*)dev_id;
        int value;
        value = gpio_get_value(key_dr.pin);
    
        if(value == 0)  /*有键按下key_desc.value = 0x8x*/
            key_value = key_dr.value|0x80;
        else 
            key_value = key_dr.value ;
    
        pressed = 1;
        wake_up_interruptible(&my_key_waitq);
    
        kill_fasync(&fasync_queue, SIGIO, POLLIN | POLLOUT);
        return 0;
    }
    
    
    static int my_key_open(struct inode *inode, struct file *file)
    {
        int ret;
        printk("request_irq setting
    ");
        ret = request_irq( IRQ_EINT(16),  my_key_irq, IRQ_TYPE_EDGE_BOTH, "KEY1", (void*)&key_desc[0]);
        if(ret) {
                printk("ret is %d
    ", ret);
                return ret;
            }
        ret = request_irq( IRQ_EINT(17),  my_key_irq, IRQ_TYPE_EDGE_BOTH, "KEY2", (void*)&key_desc[1]);
        if(ret)
            return ret;    
        ret = request_irq( IRQ_EINT(18),  my_key_irq, IRQ_TYPE_EDGE_BOTH, "KEY3", (void*)&key_desc[2]);
        if(ret)
            return ret;
        ret = request_irq( IRQ_EINT(19),  my_key_irq, IRQ_TYPE_EDGE_BOTH, "KEY4", (void*)&key_desc[3]);
        if(ret)
            return ret;    
        return 0;
    }
    
    
    /*static int my_key_open(struct inode * inode, struct file * file)
    {
        int ret;
        unsigned char i;
        for(i = 0; i < 4; i++) {
    //        s3c_gpio_cfg(key_desc[i].pin, EINT_MODE );  sizeof(key_desc) / sizeof(key_desc[0])
            ret = request_irq(key_desc[i].irq,  my_key_irq , IRQ_TYPE_EDGE_BOTH, key_desc[i].name,(void*)&key_desc[i]);
            if(ret) {
                printk(KERN_EMERG"request_irq error %d
    " , i);
                break;
            }
        }
        if(ret) {
            i--;
            for(; i > 0; i--) {
                free_irq(key_desc[i].irq, (void*)&key_desc[i]);
                return -EBUSY;
            }
        }
        return 0;
    }*/
    
    static int my_key_fasync(int fd, struct file * file, int on){
            return fasync_helper(fd, file, on, &fasync_queue);
    }
    
    
    static ssize_t my_key_read(struct file *filep, char *outbuf, size_t n, loff_t *ppos)
    {
        int ret;
        if(n != 1) {
            printk(KERN_EMERG"The driver can only give one key value once!
    ");
            return -EINVAL;
        }
    
        if(filep->f_flags & O_NONBLOCK) {
            if(!pressed)
                return -EBUSY;
        } else {
            wait_event_interruptible(my_key_waitq, pressed);/*pressed为1时继续执行后面,否则休眠*/
            pressed = 0;
    
            ret = copy_to_user(outbuf, &key_value, 1);
            if(ret) {
                printk(KERN_EMERG"key copy_to_user error
    ");
                return -ENOMEM;
            }
        }
        return 0;
    }
    
    static int my_key_write(struct file * filep,const char * buf,size_t n,loff_t * ppos)
    {
    
        return 0;
    }
    
     
    static int my_key_close(struct inode * inode,struct file * file)
    {
         free_irq(IRQ_EINT(16), (void*)&key_desc[0]);
         free_irq(IRQ_EINT(17), (void*)&key_desc[1]);
         free_irq(IRQ_EINT(18), (void*)&key_desc[2]);
         free_irq(IRQ_EINT(19), (void*)&key_desc[3]);
         my_key_fasync(-1, file, 0);
         return 0;
    }
    
    static unsigned int my_key_poll(struct file *filep, poll_table *wait)
    {
        unsigned int mask = 0;
        poll_wait(filep, &my_key_waitq, wait);
    
        if(pressed) {
            mask |= POLLIN | POLLRDNORM;
        }
    
        return mask;
    }
    
    
    
    struct file_operations key_fops= {
        .owner    =   THIS_MODULE,
        .open     =   my_key_open,
        .read     =   my_key_read,
        .write    =   my_key_write,
        .release  =   my_key_close,
        .poll     =   my_key_poll,
        .fasync   =   my_key_fasync,
    };
    
    
    
    static int __init my_key_init(void)
    {
        int ret;
        int deno;
        deno = MKDEV(keymajor, 0);
        cdev_init(&key_cdev, &key_fops);
        if (keymajor) {
            register_chrdev_region(deno, 1, KEYNAME);
            printk(KERN_EMERG"key major is %d
    ", keymajor);
        } else {
            alloc_chrdev_region(&deno, 0, 1, KEYNAME);
            keymajor = MAJOR(deno);
            printk(KERN_EMERG"key major is %d
    ", keymajor);
        }
    
        key_cdev.owner = THIS_MODULE;
        key_cdev.ops   = &key_fops;
        
        ret = cdev_add(&key_cdev, deno, 1);
        if(ret) {
            printk(KERN_EMERG"cdev_add error
    ");
            goto add_error;
        }
    
    
        key_class= class_create(THIS_MODULE, KEYNAME);
        if(IS_ERR(key_class)) {
            printk(KERN_EMERG"class_create error
    ");
            goto class_error;
        }
    
        key_device= device_create(key_class, NULL, deno, NULL, KEYNAME);
        if(IS_ERR(key_device)) {
            printk(KERN_EMERG"device_create error
    ");
            goto device_error;
        }
    
    //    init_waitqueue_head(&my_key_waitq);
        return 0;
    
        
        device_error:
            
            class_destroy(key_class);
            
        class_error:
            
            cdev_del(&key_cdev);
            
        add_error:
            
             unregister_chrdev_region(deno,1);
            
            
      return -ENODEV;
        
    }
    
    
    static void __exit my_key_exit(void)
    {
        device_destroy(key_class, MKDEV(keymajor, 0));
        class_destroy(key_class);
        cdev_del(&key_cdev);
        unregister_chrdev_region(MKDEV(keymajor, 0), 1);
    }
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("qigaohua");
    module_init(my_key_init);
    
    module_exit(my_key_exit);


    中断poll测试程序:

    #include <sys/types.h>
    
    #include <sys/stat.h>
    
    #include <fcntl.h>
    
    #include <stdio.h>
    
    #include <poll.h>
    
    #include <signal.h>
    
    
    int main()
    {
        int fd;
        int ret;
        unsigned char key_value;
        struct pollfd fds[1];
    
        
        fd = open("/dev/mykey", O_RDWR);
        if(fd < 0) {
            perror("/dev/mykey open error
    ");
            return 0;
        }
    
        fds[0].fd = fd;
        fds[0].events = POLLIN;
    
        while(1) {
            ret = poll(fds, 1, 5000);
            if(ret == 0) {
                printf("timeout
    ");
            }else {
    
                read(fd, &key_value, 1);
                printf("key_value = 0x%x
    ", key_value);
            }
        }
    }


    异步通知fasync中断测试程序:

    #include <sys/types.h>
    
    #include <sys/stat.h>
    
    #include <fcntl.h>
    
    #include <stdio.h>
    
    #include <poll.h>
    
    #include <signal.h>
    
    
    int main()
    {
        int oflags;
    
        fd = open("/dev/mykey", O_RDWR);
        if(fd < 0) {
            printf("/dev/mykey open error
    ");
            return -EBUSY;
        }
    
        signal(SIGIO, my_sinal_fun);
        fcntl(fd, F_SETOWN, getpid());
        oflags = fcntl(fd, F_GETFL);
        fcntl(fd, F_SETFL, oflags | FASYNC);
    
        while(1) {
            sleep(5);
            printf("hello
    ");
        }
    
    }
  • 相关阅读:
    mac下 brew 切换阿里镜像
    梨视频(PearVideo)下载解析的方法和技巧,梨视频下载到本地
    如何快速的下载Tumblr(汤不热)视频?操作步骤很简单,快来看看!
    什么是json? 什么是xml?JSON与XML的区别比较
    如何下载Twitter视频?最简单的保存推特视频的方法
    【收藏】轻松导出全民K歌里任何人录制的短视频(MV)、歌曲的方法
    【小白技术笔记】保存皮皮虾APP无水印视频到手机相册,只需要三步 [技术干货]
    技术干货!腾讯微视短视频去水印下载到本地的方法
    P1562 还是N皇后
    循环赛日程表
  • 原文地址:https://www.cnblogs.com/qigaohua/p/5450216.html
Copyright © 2011-2022 走看看