• 中断按键驱动程序引入异步通知


    学习目的:

    • 理解Linux中的异步通知机制
    • 加入异步通知机制,优化中断方式按键驱动程序

    前面实现了查询方式、中断方式不同按键驱动程序,测试过程中,我们发现在查询方式中,应用程序需要不断轮询获取按键状态值,这种方式极大的耗费CPU资源。中断方式中,应用程序读取按键值时,如果无按键按下,应用程序将进入休眠,直到按键按下触发中断,中断服务函数中唤醒当前应用程序,如长时间无按键按下,应用程序不被唤醒,这个过程不能执行其他任务。显然,中断方式比查询方式占用CPU资源少,但这两个驱动程序都有相关共性,都是应用程序主动的去调用函数读取按键值,有没有一种机制当有数据值可以读时,驱动程序通知应用程序,让应用程序来读取信息呢?今天我们引入的异步通知就实现了这种机制!

    1、异步通知机制原理

    异步通知机制是通过信号来实现的,大致可以概况为:应用层调用signal函数设置相应信号的捕获和信号处理函数,当驱动程序中有有效数据读取时,发送相应信号,应用程序中的信号处理函数将被系统自动调用。一般在信号处理函数中读取通知就绪的数据,这样使得无阻塞IO的应用程序不用轮询是否有数据可获取,不需要使用阻塞的IO进行休眠等待。

    根据以上描述,可以看出实现异步通知机制应用程序需做的事情包含以下两部分:

    ①、设置相关信号的捕获和信号的处理函数

    ②、信号由驱动程序来发送,但驱动程序需要知道信号发送给那个应用程序,也就是说应用程序需要告诉驱动自己的进程ID

    ③、让驱动程序支持异步机制

    除应用程序外,驱动程序也需实现以下两项内容:

    ①、获取打开设备驱动程序的应用程序的进程ID

    ②、有数据可以获取时,发送信号通知应用程序

    应用程序和驱动程序具体实现细节,如下框图所示:

    首先应用程序调用signal函数绑定信号和信号处理函数 ,调用fcntl告诉内核当前进行的ID,调用fcntl获取当前进行标识,调用fcntl设置设备文件支持FASYNC,即异步通知模式。

    驱动程序获取应用程序进程ID,file->f_owner当用户调用fcntl(fd,F_SETOWN,getpid())时,由内核自动设置。

    驱动程序中同时需实现file_operation结构体中的.fasync成员,fasync中调用fasync_helper初始化用于描述异步通知数据结构。

    驱动程序中还需实现可获取有效数据时,调用kill_fasync 函数发送信号。

    2、按键驱动程序的异步通知实现

    按照1中的分析,驱动程序中需实现 file_operation结构体中的.fasync成员和有效数据可获取时发送信号

    static int button_drv_fasync (int fd, struct file *filp, int on);
    {
        printk("driver: sixth_drv_fasync
    ");
        return fasync_helper (fd, filp, on, &button_async);
    }

    按键按下,此时可获取按键有效信息,在按键中断程序中调用kill_fasync发送信号

    static irqreturn_t button_irq_handle(int irq, void *dev_id)
    {    
        struct button_desc *pdesc = NULL;
        unsigned char pin_val;
        
        pdesc = (struct button_desc *)dev_id;
        
        pin_val = gpio_get_value(pdesc->pin);    
    
        if(pin_val == 1)
        {
            key_status = pdesc->key_val | 0x80;
        }
        else
        {
            key_status = pdesc->key_val;
        }
        
        event_trig = 1;
        wake_up_interruptible(&button_waitq); 
        
        kill_fasync (&button_async, SIGIO, POLL_IN);
        
        return IRQ_RETVAL(IRQ_HANDLED);
    }

    3、应用程序实现

    应用程序调用signal函数绑定信号和信号处理函数 ,调用fcntl告诉内核当前进行的ID,调用fcntl获取当前进行标识,调用fcntl设置设备文件支持FASYNC,即异步通知模式,主函数代码如下

    int main(int argc, char **argv)
    {
        int flags;
        
        fd = open("/dev/button", O_RDWR);
        if(fd == -1)
        {
            printf("can't open...
    ");
            exit(EXIT_FAILURE);
        }
        
        signal(SIGIO, handler_fcn); //设置信号的处理函数
        
        fcntl(fd, F_SETOWN, getpid());
        flags = fcntl(fd, F_GETFL); 
        fcntl(fd, F_SETFL, flags | FASYNC);
        
        while(1)
        {
            sleep(100);
        }
    
        exit(EXIT_SUCCESS);
    }

    驱动程序完整代码

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    #include <linux/irq.h>
    #include <asm/uaccess.h>
    #include <asm/irq.h>
    #include <asm/io.h>
    #include <plat/gpio-fns.h>
    #include <mach/gpio-nrs.h>
    #include <linux/interrupt.h>
    #include <linux/wait.h>
    #include <linux/sched.h>
    #include <linux/device.h>
    #include <linux/gpio.h>
    #include <linux/poll.h>
    
    #define BUTTON_NUMS    4
    #define IRQT_BOTHEDGE IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
    
    static int major;
    static int event_trig = 0;
    
    static unsigned char key_status;
    
    static volatile unsigned long *gpfcon = NULL;
    static volatile unsigned long *gpgcon = NULL;
    static volatile unsigned long *gpfdat = NULL;
    static volatile unsigned long *gpgdat = NULL;
    
    static struct class *button_drv_class;
    static struct class_device    *button_drv_class_dev;
    
    static struct fasync_struct *button_async;
    
    static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
    
    struct button_desc
    {
        int pin;
        int irq_type;
        unsigned long flags;
        char *name;
        int key_val;
    };
    
    static struct button_desc btn_desc[BUTTON_NUMS] = {
        {S3C2410_GPF(0),  IRQ_EINT0,  IRQT_BOTHEDGE, "S2", 1},
        {S3C2410_GPF(2),  IRQ_EINT2,  IRQT_BOTHEDGE, "S3", 2},
        {S3C2410_GPG(3),  IRQ_EINT11, IRQT_BOTHEDGE, "S4", 3},
        {S3C2410_GPG(11), IRQ_EINT19, IRQT_BOTHEDGE, "S5", 4},
    };
    
    static int button_drv_open(struct inode *inode, struct file *file);
    static ssize_t button_drv_read(struct file *file, char __user *buf, size_t count, loff_t *ppos);
    static ssize_t button_drv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos);
    static int button_drv_close(struct inode *inode, struct file *file);
    static unsigned button_drv_poll(struct file *file, poll_table *wait);
    static int button_drv_fasync (int fd, struct file *filp, int on);
    
    
    struct file_operations button_drv_fileop = {
        .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
        .open   =   button_drv_open,
        .read   =   button_drv_read,
        .write  =   button_drv_write,
        .release =  button_drv_close,
        .poll = button_drv_poll,
        .fasync = button_drv_fasync,
    };
    
    static irqreturn_t button_irq_handle(int irq, void *dev_id)
    {    
        struct button_desc *pdesc = NULL;
        unsigned char pin_val;
        
        pdesc = (struct button_desc *)dev_id;
        
        pin_val = gpio_get_value(pdesc->pin);    
    
        if(pin_val == 1)
        {
            key_status = pdesc->key_val | 0x80;
        }
        else
        {
            key_status = pdesc->key_val;
        }
        
        event_trig = 1;
        wake_up_interruptible(&button_waitq); 
        
        kill_fasync (&button_async, SIGIO, POLL_IN);
        
        return IRQ_RETVAL(IRQ_HANDLED);
    }
    
    static int button_drv_open(struct inode *inode, struct file *file)
    {
        int i;
        
        *gpfcon &= ~((0x3<<(0*2)) | (0x3<<(2*2)));
        *gpgcon &= ~((0x3<<(3*2)) | (0x3<<(11*2)));
        
        /* 注册中断处理函数 */
        for(i = 0; i < BUTTON_NUMS; i++)
            request_irq(btn_desc[i].irq_type, button_irq_handle, btn_desc[i].flags, btn_desc[i].name, &btn_desc[i]);
        
        return 0;
    }
    
    static ssize_t button_drv_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
    {
        if(count != 1)
            return EINVAL;
        
        wait_event_interruptible(button_waitq, event_trig);
        
        if(copy_to_user(buf, &key_status, count))
            return EFAULT;
        
        event_trig = 0;
        return 0;
    }
    
    static ssize_t button_drv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
    {
        printk("button_drv_write
    ");
        
        return 0;
    }
    
    static int button_drv_close(struct inode *inode, struct file *file)
    {
        int i;
        
        for(i = 0; i < BUTTON_NUMS; i++)
            free_irq(btn_desc[i].irq_type, &btn_desc[i]);
        
        return 0;
    }
    
    static unsigned button_drv_poll(struct file *file, poll_table *wait)
    {
        unsigned int mask = 0;
        poll_wait(file, &button_waitq, wait); // 不会立即休眠
    
        if (event_trig)
            mask |= POLLIN | POLLRDNORM;
    
        return mask;
    }
    
    static int button_drv_fasync (int fd, struct file *filp, int on)
    {
        printk("driver: sixth_drv_fasync
    ");
        return fasync_helper (fd, filp, on, &button_async);
    }
            
    static int button_drv_init(void)
    {
        major = register_chrdev(0, "button_light", &button_drv_fileop);
        
        button_drv_class = class_create(THIS_MODULE, "button_drv");
        //button_drv_class_dev = class_device_create(button_drv_class, NULL, MKDEV(major, 0), NULL, "button"); /* /dev/button */
        button_drv_class_dev = device_create(button_drv_class, NULL, MKDEV(major, 0), NULL, "button"); /* /dev/button */
        
        gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
        gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
        gpfdat = gpfcon + 1;
        gpgdat = gpgcon + 1;
        
        return 0;
    }
    
    static void button_drv_exit(void)
    {
        unregister_chrdev(major, "button_drv");
        
        //class_device_unregister(button_drv_class_dev);
        device_unregister(button_drv_class_dev);
        class_destroy(button_drv_class);
        
        iounmap(gpfcon);
        iounmap(gpgcon);
    }
    
    module_init(button_drv_init);
    module_exit(button_drv_exit);
    
    MODULE_LICENSE("GPL");
    irq_button_drv.c

    应用程序完整代码

    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/fcntl.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <poll.h>
    #include <signal.h>
    
    int fd;
    
    void handler_fcn(int signum)
    {
        int ret;
        unsigned char key_buf;
        
        ret = read(fd, &key_buf, 1);
        if(ret < 0)
        {
            printf("read err...
    ");
            exit(EXIT_FAILURE);
        }
        /* 判断有按键按下,打印按键信息 */
        printf("key_val=0x%x
    ", key_buf);
    }
    
    int main(int argc, char **argv)
    {
        int flags;
        
        fd = open("/dev/button", O_RDWR);
        if(fd == -1)
        {
            printf("can't open...
    ");
            exit(EXIT_FAILURE);
        }
        
        signal(SIGIO, handler_fcn); //设置信号的处理函数
        
        fcntl(fd, F_SETOWN, getpid());
        flags = fcntl(fd, F_GETFL); 
        fcntl(fd, F_SETFL, flags | FASYNC);
        
        while(1)
        {
            sleep(100);
        }
    
        exit(EXIT_SUCCESS);
    }
    irq_button_test.c

    测试结果

     

  • 相关阅读:
    Balanced Number [ZOJ 3416]
    动态树
    Jason的特殊爱好 [FZU 2113]
    Raney引理
    Tri Tiling [POJ 2663]
    糖尿病的虾青素+胰岛素疗法 (转)
    JAVASCRIPT 开发工具:aptana ,WebStorm
    众志和达,英文SOUL 存储与数据安全提供商
    健身音乐及其它
    nodejs + CompoundJS 资源
  • 原文地址:https://www.cnblogs.com/053179hu/p/13726439.html
Copyright © 2020-2023  润新知