按键驱动的恩恩怨怨之异步通知

来源:互联网 发布:js延迟执行方法 编辑:程序博客网 时间:2024/05/17 07:10

       转载请注明出处:http://blog.csdn.net/ruoyunliufeng/article/details/24326603

       说起异步通知,简单点的理解就是:以前都是应用程序主动看按键是否按下云云的。。。这回应用程序架子大了。说老子才不去呢。把任务给了驱动。然后驱动发现按键按下,屁颠屁颠的去通知应用程序。

一.驱动代码

      如果你看了前几篇文章,这个代码对你来说是非常简单的,所改动的东西非常的少。

#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 <asm/arch/regs-gpio.h>#include <asm/hardware.h>#include <linux/poll.h>static struct class *fifthdrv_class;static struct class_device*fifthdrv_class_dev;//volatile unsigned long *gpfcon;//volatile unsigned long *gpfdat;static DECLARE_WAIT_QUEUE_HEAD(button_waitq);/* 中断事件标志, 中断服务程序将它置1,fifth_drv_read将它清0 */static volatile int ev_press = 0;static struct fasync_struct *button_async;  //定义一个结构struct pin_desc{                  //定义结构体unsigned int pin;unsigned int key_val;};/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 *//* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */static unsigned char key_val;/* * K1,K2,K3,K4对应GPG0,GPG3,GPG5,GPG6 */struct pin_desc pins_desc[4] = {     //定义一个结构体数组{S3C2410_GPG0, 0x01},{S3C2410_GPG3, 0x02},{S3C2410_GPG5, 0x03},{S3C2410_GPG6, 0x04},};/*  * 确定按键值  */static irqreturn_t buttons_irq(int irq, void *dev_id)          //参数中断号,和ID{struct pin_desc * pindesc = (struct pin_desc *)dev_id;    //?定义一个结构体指针使他的初值为IDunsigned int pinval;pinval = s3c2410_gpio_getpin(pindesc->pin);            //系统函数独处引脚值(GPF0)if (pinval){/* 松开 */key_val = 0x80 | pindesc->key_val;}else{/* 按下 */key_val = pindesc->key_val;}    ev_press = 1;                  /* 表示中断发生了 */    wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */kill_fasync (&button_async, SIGIO, POLL_IN);   //发送信号return IRQ_RETVAL(IRQ_HANDLED);}static int fifth_drv_open(struct inode *inode, struct file *file){/* GPG0,GPG3,GPG5,GPG6为中断引脚: EINT8,EINT11,EINT13,EINT14 */request_irq(IRQ_EINT8,  buttons_irq, IRQT_BOTHEDGE, "K1", &pins_desc[0]);request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "K2", &pins_desc[1]);request_irq(IRQ_EINT13, buttons_irq, IRQT_BOTHEDGE, "K3", &pins_desc[2]);request_irq(IRQ_EINT14, buttons_irq, IRQT_BOTHEDGE, "K4", &pins_desc[3]);return 0;}ssize_t fifth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos){if (size != 1)return -EINVAL;/* 如果没有按键动作, 休眠 */wait_event_interruptible(button_waitq, ev_press);/* 如果有按键动作, 返回键值 */copy_to_user(buf, &key_val, 1);ev_press = 0;return 1;}int fifth_drv_close(struct inode *inode, struct file *file)     //出链,禁止中断{free_irq(IRQ_EINT8,  &pins_desc[0]);free_irq(IRQ_EINT11, &pins_desc[1]);free_irq(IRQ_EINT13, &pins_desc[2]);free_irq(IRQ_EINT14, &pins_desc[3]);return 0;}static unsigned fifth_drv_poll(struct file *file, poll_table *wait){unsigned int mask = 0;poll_wait(file, &button_waitq, wait); // 不会立即休眠,只是把进程挂到队列里面去if (ev_press)                          //判断是否有数据返回。有的话进行赋值,没有的话休眠mask |= POLLIN | POLLRDNORM;    //返回位掩码, 它描述哪个操作可马上被实现。return mask;}static int fifth_drv_fasync (int fd, struct file *filp, int on)   {printk("driver: fifth_drv_fasync\n");             //为了说明次函数被调用增加一条打印语句return fasync_helper (fd, filp, on, &button_async); //初始化定义的结构体}static struct file_operations sencod_drv_fops = {    .owner   =  THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */    .open    =  fifth_drv_open,     .read =fifth_drv_read,   .release =  fifth_drv_close,.poll    =  fifth_drv_poll,.fasync =  fifth_drv_fasync,};int major;static int fifth_drv_init(void){major = register_chrdev(0, "fifth_drv", &sencod_drv_fops);fifthdrv_class = class_create(THIS_MODULE, "fifth_drv");fifthdrv_class_dev = class_device_create(fifthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons *///gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);//gpfdat = gpfcon + 1;return 0;}static void fifth_drv_exit(void){unregister_chrdev(major, "fifth_drv");class_device_unregister(fifthdrv_class_dev);class_destroy(fifthdrv_class);//iounmap(gpfcon);return 0;}module_init(fifth_drv_init);module_exit(fifth_drv_exit);MODULE_LICENSE("GPL");



二.应用程序代码

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>#include <poll.h>#include <signal.h>#include <sys/types.h>#include <unistd.h>#include <fcntl.h>/* fifthdrvtest   */int fd;void my_signal_fun(int signum)    //信号处理函数{unsigned char key_val;read(fd, &key_val, 1);       //读取按键值printf("key_val: 0x%x\n", key_val);  //打印}int main(int argc, char **argv){unsigned char key_val;int ret;int Oflags;signal(SIGIO, my_signal_fun); //注册信号处理函数fd = open("/dev/buttons", O_RDWR);if (fd < 0){printf("can't open!\n");}fcntl(fd, F_SETOWN, getpid());      // 告诉内核,发给谁(通过PID)Oflags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, Oflags | FASYNC);  // 改变fasync标记,最终会调用到驱动的faync > fasync_helper:初始化/释放fasync_structwhile (1){sleep(1000);}return 0;}




三.分析

           1.应用程序

        首先来看看应用程序都干了些什么。应用程序首先注册了信号处理函数(函数的处理主要写在这里),然后打开驱动。主函数中while(1)中是个循环,一直睡眠。当中断发生时候,驱动会把信号发给应用程序,应用程序在信号处理函数进行处理。fcntl(fd, F_SETOWN, getpid());

                                Oflags = fcntl(fd, F_GETFL);
                                fcntl(fd, F_SETFL, Oflags | FASYNC);
这三段代码是应用程序实现异步通知的机制所在。

用户程序必须执行 2 个步骤来使能来自输入文件的异步通知. 首先, 它们指定一个进程作为文件的拥有者. 当一个进程使用 fcntl 系统调用发出 F_SETOWN 命令, 这个拥有者进程的 ID 被保存在 filp->f_owner 给以后使用. 这一步对内核知道通知谁是必要的. 为了真正使能异步通知, 用户程序必须设置 FASYNC 标志在设备中, 通过 F_SETFL fcntl 命令.

在这 2 个调用已被执行后, 输入文件可请求递交一个 SIGIO 信号, 无论何时新数据到达. 信号被发送给存储于 filp->f_owner 中的进程(或者进程组, 如果值为负值).


(说的有点可能难以理解,没关系最后会用一张图给小伙伴们说明其中的道理)


           2.驱动程序

1. 当发出 F_SETOWN, 什么都没发生, 除了一个值被赋值给 filp->f_owner.(内核完成的)

2.当 F_SETFL 被执行来打开 FASYNC, 驱动的 fasync 方法被调用.

static int fifth_drv_fasync (int fd, struct file *filp, int on)   {printk("driver: fifth_drv_fasync\n");             //为了说明次函数被调用增加一条打印语句return fasync_helper (fd, filp, on, &button_async); //初始化定义的结构体}
3. 当数据到达, 所有的注册异步通知的进程必须被发出一个 SIGIO 信号.

kill_fasync (&button_async, SIGIO, POLL_IN);  
到这里应用程序就接收到信号了,执行相应的操作了。
       

           3.图片

你发现怎么我按下一次出现了了好几个0x1啊。卖个关子,下面几篇会有讲诉。



2 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 手机百度云和谐怎么办 服务器连接不上怎么办 微粒贷请求失败怎么办 房子里手机信号不好怎么办 屋里上网没信号怎么办 屋子里面没信号怎么办 crm服务器关机了怎么办 日本代金券诈骗怎么办 发票已认证 作废怎么办 淘宝不发发票怎么办 ipd密码忘记了怎么办 apple id闪退怎么办 信用卡持卡人死亡欠款怎么办 信用卡名字错了怎么办 ios超出手机内存怎么办 app id停用了怎么办 相机储存空间不足怎么办 苹果icloud8满了怎么办 红米云空间已满怎么办 忘记手机密码怎么办oppo oppo云相册丢失怎么办 oppoa79密码忘了怎么办 云存储空间满了怎么办 云备份空间不足怎么办 安卓没有中文怎么办 燃气表显示异常怎么办 is语音登录不了怎么办 is语音禁止登录怎么办 淘宝竞争不过同行怎么办 碰到比价的顾客怎么办 淘宝同行恶意捣乱怎么办 怀孕吃了打虫药怎么办 阿迪鞋微信中签不能取怎么办 康乃馨花叶长斑怎么办 杜鹃花叶长斑怎么办 青苹果竹芋烂根怎么办 朱槿花叶子发黄怎么办 猫眼叶子卷了怎么办 鞋底氧化变黄怎么办 白色旅游鞋变黄怎么办 如意皇后不张新叶片怎么办