码迷,mamicode.com
首页 > 其他好文 > 详细

字符设备之异步通信

时间:2015-03-21 12:44:19      阅读:121      评论:0      收藏:0      [点我收藏+]

标签:

基于字符设备驱动之中断按键来进行分析字符设备驱动的另一种技巧:异步通知--一种可以让驱动程序变的很主动的方法
一、目标:
按键按下时,驱动层序主动通知应用程序有数据可读,这样就不用应用程序老是自己主动去读数据,专心做自己的事,该来的不用去请都会自己送上门来,瞬间就高大上起来啦
要思考的问题:
①注册信号处理函数
②谁发信号?内核驱动
③发给谁?APP。前提是App要告诉驱动它的PID

④怎么发?kell_fasync()函数

二、首先在ubuntu上感受一下这种服务:
(1)man signal 查看signal函数的使用方法:
SYNOPSIS

#include <signal.h>

typedef void (*sighandler_t)(int);//sighandler_t是一种函数指针,他的返回值为void型,参数是int

sighandler_t signal(int signum, sighandler_t handler);

DESCRIPTION

signal()  sets  the  disposition  of  the  signal signum to handler, which is

either SIG_IGN, SIG_DFL, or the address of a programmer-defined  function  (a

"signal handler").

意思是:signal函数是把sigunm值传给handler函数的,定义的handler函数要接收一个参数signum
(2)程序内容:fasync_tesc.c文件

//编译指令:gcc -o fasync fasync_test.c
#include <stdio.h>
#include <signal.h>

void sig_handle(int signum) //信号值
{
	printf("Running in sig_handle\n");
}

int main()
{
	signal(SIGUSR1,sig_handle);
	while(1)
	{
		sleep(1000);
	}
	return 0;
}
(3)运行程序&发送信号
./fasync &    //后台运行
ps    //查看当前正在运行的进程,找出PID值
使用kill向进程发送信号
man kill
kill [ -signal | -s signal ] pid ...
kill 信号 pid    意思是:把信号发给pid对应进程
kill -USR1 pid
打印结果

技术分享

*******************************************************************************************************************************************************************************************

三、ARM-linux应用编程
app要做的事:注册信号处理函数、告诉驱动程序他自己的PID
应用程序调用的函数是fcntl(),在文件里边有说明用法

app_fasync.c

/* 文件的编译指令是arm-linux-gcc -static -o app_irq app_fasync.c */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>   //sleep 
#include <stdio.h>
#include <signal.h>

int fd;
void signal_handler(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)
{
	int flag; 
	unsigned char key_val;
	
	signal(SIGIO,signal_handler);//注册进程的信号处理函数,处理函数接收SIGIO类型信号
	
	fd = open("/dev/buttons", O_RDWR); //申请外部引脚中断服务
	if (fd < 0)
	{
		printf("can't open!\n");
	}
	//①app告诉内核自己的pid:F_SETOWN表示此时fcntl()函数进行“信号发给谁?”的前提操作,app告诉fd指向的设备驱动“我的PID是个getpid()”
	fcntl(fd, F_SETOWN, getpid());
	//②读当前文件的状态
	flag = fcntl(fd,F_GETFL); 
	//③修改当前文件的状态,添加异步通知功能,添加的前提是不要改变原来的一些状态,所以上一步是要先读出来,然后在这里来一个位或
	fcntl(fd,F_SETFL,flag | FASYNC);
	while (1)
	{
		sleep(1000);
	}
	return 0;
}
四、驱动编程
要做的事:有数据时通过kell_fasync()函数发信号给pid对应的应用层的进程
fasync.c文件

#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/module.h>
#include <linux/device.h> 		//class_create
#include <mach/regs-gpio.h>		//S3C2410_GPF1 
#include <mach/hardware.h>
#include <linux/interrupt.h>  //wait_event_interruptible
#include <linux/fs.h>
#include <linux/fcntl.h>

/* 定义并初始化等待队列头 */
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

static struct class *buttondev_class;
static struct device *buttons_device;

static struct pin_desc{
	unsigned int pin;
	unsigned int key_val;
};

static struct pin_desc pins_desc[4] = {
		{S3C2410_GPF1,0x01}, //S3C2410_GPF1是对GPF1引脚这种“设备”的编号dev_id
		{S3C2410_GPF4,0x02},
		{S3C2410_GPF2,0x03},
		{S3C2410_GPF0,0x04},
}; 
static int ev_press = 0;

static unsigned char key_val;
int major;

static struct fasync_struct *button_fasync;//button_fasync结构体里包含了发给谁(PID指定) 

/* 中断处理函数 */
static irqreturn_t handle_irq(int irq, void *dev_id)
{
	struct pin_desc *irq_pindesc = (struct pin_desc *)dev_id;//
	unsigned int pinval;
		
	pinval = s3c2410_gpio_getpin(irq_pindesc->pin);//获取按键值:有按键按下返回按键值0
	/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
	/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
	if(pinval)
	{
		/* 松开 */
		key_val = 0x80 | (irq_pindesc->key_val);
	}
	else
	{
		/* 按下 */
		key_val = irq_pindesc->key_val;
	}

	ev_press = 1;							/* 表示中断已经发生 */
	//wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */ //没有休眠就没有唤醒 nice!!!
	kill_fasync(&button_fasync, SIGIO, POLL_IN);  //发信号给app,POLL_IN是发信号原因,SIGIO表示要发送的信号类型
	return IRQ_HANDLED;
}

static int buttons_dev_open(struct inode * inode, struct file * filp)
{
	/*  K1-EINT1,K2-EINT4,K3-EINT2,K4-EINT0
  	 *  配置GPF1、GPF4、GPF2、GPF0为相应的外部中断引脚
  	 *  IRQT_BOTHEDGE应该改为IRQ_TYPE_EDGE_BOTH
	 */
	request_irq(IRQ_EINT1, handle_irq, IRQ_TYPE_EDGE_FALLING, "K1",&pins_desc[0]);
	request_irq(IRQ_EINT4, handle_irq, IRQ_TYPE_EDGE_FALLING, "K2",&pins_desc[1]);
	request_irq(IRQ_EINT2, handle_irq, IRQ_TYPE_EDGE_FALLING, "K3",&pins_desc[2]);
	request_irq(IRQ_EINT0, handle_irq, IRQ_TYPE_EDGE_FALLING, "K4",&pins_desc[3]);
	return 0;
}

static int buttons_dev_close(struct inode *inode, struct file *file)
{
	free_irq(IRQ_EINT1,&pins_desc[0]);
	free_irq(IRQ_EINT4,&pins_desc[1]);
	free_irq(IRQ_EINT2,&pins_desc[2]);
	free_irq(IRQ_EINT0,&pins_desc[3]);
	return 0;
}

static ssize_t buttons_dev_read(struct file *file, char __user *user, size_t size,loff_t *ppos)
{
	if (size != 1)
			return -EINVAL;
	
	/* 当没有按键按下时,休眠。
	 * 即ev_press = 0;
	 * 当有按键按下时,发生中断,在中断处理函数会唤醒
	 * 即ev_press = 1; 
	 * 唤醒后,接着继续将数据通过copy_to_user函数传递给应用程序
	 */
	//wait_event_interruptible(button_waitq, ev_press);//从此这里就不需要为了等待而睡眠了
	copy_to_user(user, &key_val, 1);
	
	/* 将ev_press清零 */
	ev_press = 0;
	return 1;	
}
static int button_drv_fasync(int fd, struct file *filp, int on)  
{  
    return fasync_helper(fd, filp, on, &button_fasync);  //初始化/释放fasync_struct 
}  
/* File operations struct for character device */
static const struct file_operations buttons_dev_fops = {
	.owner		= THIS_MODULE,
	.open		= buttons_dev_open,
	.read		= buttons_dev_read,
	.release    = buttons_dev_close,
	.fasync     = button_drv_fasync, //当应用程序调用了fcntl(fd, F_SETFL, Oflags | FASYNC);  最终会调用驱动的fasync函数即button_drv_fasync
};

/* 驱动入口函数 */
static int buttons_dev_init(void)
{
	/* 主设备号设置为0表示由系统自动分配主设备号 */
	major = register_chrdev(0, "buttons_dev", &buttons_dev_fops);

	/* 创建buttondev类 */
	buttondev_class = class_create(THIS_MODULE, "buttondev");

	/* 在buttondev类下创建buttons设备,供应用程序打开设备*/
	buttons_device = device_create(buttondev_class, NULL, MKDEV(major, 0), NULL, "buttons");//

	return 0;
}

/* 驱动出口函数 */
static void buttons_dev_exit(void)
{
	unregister_chrdev(major, "buttons_dev");
	device_unregister(buttons_device);  //卸载类下的设备
	class_destroy(buttondev_class);		//卸载类
}

/* 模块加载和卸载函数的修饰 */
module_init(buttons_dev_init);  
module_exit(buttons_dev_exit); 

MODULE_AUTHOR("CLBIAO");
MODULE_DESCRIPTION("Just for Demon");
MODULE_LICENSE("GPL");  //遵循GPL协议

测试结果:

技术分享


五、小结
驱动支持异步通知机制编程步骤:
①:给应用层提供API接口

static const struct file_operations xxx_dev_fops={

...

.fasync     = xxx_drv_fasync,  //给应用层调用,用来初始化fasync_struct结构体

...

}

②:应用层API-的底层实现:用于设置fasync_struct,告诉驱动“电话号码”,有事时才来找我
static struct fasync_struct *xxx_button_fasync;//进程会把PID“电话号码”传到这里边来
static int xxx_drv_fasync(int fd, struct file *filp, int on)  

return fasync_helper(fd, filp, on, &xxx_fasync_struct);  //初始化/释放fasync_struct

应用测试编程:
①注册进程的信号处理函数
signal(SIGIO,signal_handler);
②编写信号处理函数:把要获取资源的操作放在这里面,通常是read()或操作函数
void signal_handler(int signum)  
{  

...

action

... 

}  
③主动告诉内核“电话号码”,让他有事时通知一下
int fd;//进程设备文件
int flag;//用来保存进程在添加异步通知标识前的标识
fcntl(fd, F_SETOWN, getpid());
flag = fcntl(fd,F_GETFL); 
fcntl(fd,F_SETFL,flag | FASYNC);



字符设备之异步通信

标签:

原文地址:http://blog.csdn.net/clb1609158506/article/details/44514285

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!