按键驱动的恩恩怨怨之异步通知
来源:互联网 发布: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啊。卖个关子,下面几篇会有讲诉。
- 按键驱动的恩恩怨怨之异步通知
- 按键驱动的恩恩怨怨之概述
- 按键驱动的恩恩怨怨之查询方式
- 按键驱动的恩恩怨怨之中断方式
- 按键驱动的恩恩怨怨之poll方式
- 按键驱动的恩恩怨怨之防抖动
- 按键驱动的恩恩怨怨之同步互斥阻塞
- 嵌入式linux之按键驱动,异步通知
- 基于异步通知机制的按键驱动
- linux字符驱动之异步通知按键驱动
- linux字符驱动之异步通知按键驱动
- linux字符驱动之异步通知按键驱动
- linux字符驱动之异步通知按键驱动
- linux字符驱动之异步通知按键驱动
- 在按键驱动中增加异步通知的例子
- tiny6410_按键中断驱动_异步通知
- 按键驱动深化-异步通知机制
- tiny6410按键驱动(六)---异步通知
- 我
- use-web-fiddler
- 函数指针
- java中读xml文件,获取文件本身所在的磁盘位置
- ubuntukylin一些小工具
- 按键驱动的恩恩怨怨之异步通知
- sublime text 3 插件推荐
- HDU3371
- SQLSERVER not in 的问题
- 限制公司内部对网络设备的管理,只允许有权限的人员管理
- PE结构导出表详解
- C语言拾遗:数据区,代码区,堆区,栈区
- 图像的区域标注
- atoi和itoa函数的实现