您的位置:首页 > 其它

字符设备驱动程序之异步通知

2014-09-21 20:48 176 查看
   前面已经讲了3种读取键值的方法,分别是:① 查询方式,缺点--耗资源;② 中断方式,缺点--read()方法会一直等待按键按下,否则不会返回;③ poll机制,优点---在指定时间内返回。

   它们都有一个共同点,就是---都是应用程序主动去读取键值,那有没有一种方法是当按键按下时驱动主动提醒应用程序去读取键值呢?

答案是肯定的,这种方式就叫做异步通知,它通过发信号来实现。

扩展知识:进程间如何发送信号?

先用一个小例子来演示一下:
#include <stdio.h>
#include <signal.h>
void my_signal_fun(int signum){
static int cnt=0;
printf("signal = %d,%d times\n",signum,cnt);
}
int main(int argc,char **argv){
signal(SIGUSR1,my_signal_fun);
while(1){
sleep(1000);
}
return 0;
}
然后,编译运行这个应用程序。那怎么给这个应用程序发信号呢?

可以用命令:#kill   -信号    应用程序PID

用#ps  命令可以看到这个应用程序的PID是833,所以执行:# kill  -10  833   就会打印程序中设定的字符串。   

这节要达到的目标---按下按键时,驱动程序通知应用程序读取按键值。

问1: 怎么实现这个基于信号处理的驱动呢?

答:要确定并完成以下几点,

① 应用程序---注册信号处理函数;

② 谁来发信号?驱动发

③ 信号发给谁?发给应用程序,应用程序要告诉驱动它的PID  

④ 怎么发出信号?用 kill_fasync()

下面,我们就从代码上来看看这个驱动怎么写》

1. 定义一个 fasync_struct 结构
static struct fasync_struct *button_async;

2. 定义一个函数来初始化fasync_struct 结构
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);
}
这个函数在什么时候执行呢,当应用程序调用 .fasync    时执行。

3. 将 fifth_drv_fasync 加入到 file_operations 结构
static struct file_operations sencod_drv_fops = {
.owner   =  THIS_MODULE,
.open    =  fifth_drv_open,
.read  =    fifth_drv_read,
.release =  fifth_drv_close,
.poll    =  fifth_drv_poll,
.fasync      =  fifth_drv_fasync,
};
4.  在中断处理函数中 发送信号 
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
struct pin_desc * pindesc = (struct pin_desc *)dev_id;
unsigned int pinval;
pinval = s3c2410_gpio_getpin(pindesc->pin);
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);
}
  只要改以上几步,一个简单的驱动就可以用了。

下面看看应用测试程序怎么写:

为了使设备支持异步通知机制,驱动程序中涉及以下3项工作:

1. 支持 F_SETOWN命令,能在这个控制命令处理中设置  flip->f_owner  为对应进程ID。

  不过此项工作已由内核完成,设备驱动无须处理。

2. 支持 F_SETGL 命令的处理,每当FASYNC标志改变时,驱动程序中的fasync()函数将得以执行。
所以 在应用程序中要执行这三步:

fcntl(fd,F_SETOWN,pid);

Oflags=fcntl(fd,F_GETFL);

fcntl(fd,F_SETGL,oflags | FASYNC);  //当执行这步时,驱动中   file_operations .fasync  就执行。

应用程序代码:
#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>
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);  //一般用 SIGIO信号,表示IO口有数据
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);  //改变fasyc标识,最终会调用到faync-->fasync_helper
while (1)
{
sleep(1000);
}
return 0;
}
小知识:
  当不知道某个系统函数怎么用的时,如应包含什么头文件等,可以在linux中执行命令:#man  函数名     查看,就会看到它应包含什么头文件。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: