竞争的关键驱动的异步通知

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

说起异步通知,简单点的理解就是:曾经都是应用程序主动看按键是否按下云云的。

这回应用程序架子大了。说老子才不去呢。

把任务给了驱动。

然后驱动发现按键按下。屁颠屁颠的去通知应用程序。

一.驱动代码

假设你看了前几篇文章。这个代码对你来说是很easy的,所修改的东西很的少。

#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;    //?

定义一个结构体指针使他的初值为ID
	unsigned 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_struct

	while (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啊。卖个关子,以下几篇会有讲诉。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcnVveXVubGl1ZmVuZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" >

版权声明:本文博客原创文章,博客,未经同意,不得转载。

时间: 2024-10-25 08:19:56

竞争的关键驱动的异步通知的相关文章

arm驱动linux异步通知与异步IO【转】

转自:http://blog.csdn.net/chinazhangzhong123/article/details/51638793 <[ arm驱动] linux异步通知与 异步IO>涉及内核驱动函数二个,内核结构体一个,分析了内核驱动函数二个:可参考的相关应用程序模板或内核驱动模板二个,可参考的相关应用程序模板或内核驱动三个 描述:设备文件IO访问:阻塞与非阻塞io访问,poll函数提供较好的解决设备访问的机制,但是如果有了异步通知整套机制就更加完整了 一.阻塞 I/O,非阻塞IO,异步

深入浅出~Linux设备驱动之异步通知和异步I/O

在设备驱动中使用异步通知可以使得对设备的访问可进行时,由驱动主动通知应用程序进行访问.因此,使用无阻塞I/O的应用程序无需轮询设备是否可访问,而阻塞访问也可以被类似“中断”的异步通知所取代.异步通知类似于硬件上的“中断”概念,比较准确的称谓是“信号驱动的异步I/O". 1.异步通知的概念和作用 影响:阻塞--应用程序无需轮询设备是否可以访问 非阻塞--中断进行通知 即:由驱动发起,主动通知应用程序 2.linux异步通知编程 2.1 linux信号 作用:linux系统中,异步通知使用信号来实现

linux驱动的异步通知(kill_fasync,fasync)---- 驱动程序向应用程序发送信号

应用程序 [cpp] view plain copy #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 <fcn

《Linux4.0设备驱动开发详解》笔记--第九章:Linux设备驱动中的异步通知与同步I/O

在设备驱动中使用异步通知可以使得对设备的访问可进行时,由驱动主动通知应用程序进行访问.因此,使用无阻塞I/O的应用程序无需轮询设备是否可访问,而阻塞访问也可以被类似"中断"的异步通知所取代.异步通知类似于硬件上的"中断"概念,比较准确的称谓是"信号驱动的异步I/O". 9.1 异步通知的概念和作用 异步通知:一旦设备就绪,则主动通知应用程序,该应用程序无需查询设备状态 几种通知方式比较: 阻塞I/O :一直等待设备可访问后开始访问 非阻塞I/O:

linux中驱动异步通知应用程序的方法

驱动程序运行在内核空间中,应用程序运行在用户空间中,两者是不能直接通信的.但在实际应用中,在设备已经准备好的时候,我们希望通知用户程序设备已经ok,用户程序可以读取了,这样应用程序就不需要一直查询该设备的状态,从而节约了资源,这就是异步通知.好,那下一个问题就来了,这个过程如何实现呢?简单,两方面的工作. 一 驱动方面:1. 在设备抽象的数据结构中增加一个struct fasync_struct的指针2. 实现设备操作中的fasync函数,这个函数很简单,其主体就是调用内核的fasync_hel

Smart20学习记录----异步通知

异步通知: 阻塞与非阻塞访问.poll()函数提供了较好地解决设备访问的机制(应用程序主动访问) 异步通知:一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这一点非常类似于硬件上“中断”的概念,比较准确的称谓是“信号驱动的异步 I/O” 阻塞 I/O 意味着一直等待设备可访问后再访问,非阻塞 I/O 中使用 poll()意味着查询设备是否可访问,而异步通知则意味着设备通知自身可访问,实现了异步 I/O.由此可见,这几种方式 I/O可以互为补充. 阻塞.非阻塞 I/O.异步

Linux的fasync驱动异步通知详解

工作项目用有个需求是监测某个GPIO输入方波的频率!通俗的讲就是一个最最简单的测方波频率的示波器!不过只是测方波的频率!频率范围是0~200HZ,而且频率方波不是一直都是200HZ,大多数的时候可能一直是0或者一个更低频率的方波!同时要考虑到方波有可能一直维持在200HZ ,同时保持效率和性能的情况下,fasync驱动异步通知是个不错的选择,当初写demo的时候实测1K的方波完全没有问题!应用到项目中也是完全能满足需求!驱动很简单!业余时间把自己之前学到的知识总结一下!对自己也是个提高! 根据需

Linux的fasync驱动异步通知详解【转】

本文转载自:http://blog.csdn.net/coding__madman/article/details/51851338 版权声明:本文为博主原创文章,未经博主允许不得转载. 工作项目用有个需求是监测某个GPIO输入方波的频率!通俗的讲就是一个最最简单的测方波频率的示波器!不过只是测方波的频率!频率范围是0~200HZ,而且频率方波不是一直都是200HZ,大多数的时候可能一直是0或者一个更低频率的方波!同时要考虑到方波有可能一直维持在200HZ ,同时保持效率和性能的情况下,fasy

按键驱动异步通知

在此以前,我们都是让应用程序主动去读按键的状态,有没有一种情况,当驱动程序有数据时,主动去告诉应用程序,告诉它,有数据了,你赶紧来读吧.这种情况在linux里的专业术语就叫异步通知. 在按键的例子中异步通知可以理解为:当按键按下时,驱动程序会提醒(即触发)应用程序(通过信号signal来实现). 举一个例子:进程之间发信号 原来我们常用  kill 这个命令 : kill       -9    pid   kill这个命令就是一个发信号 发送者  :   kill 接收者  :   pid 信