linux设备驱动开发——异步通知

 在介绍异步通知前先说下阻塞和非阻塞,阻塞式I/O是一直等待直到设备可以访问消耗cpu资源,非阻塞式I/O是定期轮询设备是否可以访问即poll机制所实现的。 其实异步通知很好理解,一旦设备准备好,就主动通知应用程序,这种情况下应用程序就不需要查询设备状态 , 特像硬件上常提的“中断的概念”。 比较准确的说法其实应该叫做“信号驱动的异步I/O”,信号是在软件层次上对中断机制的一种模拟。 阻塞I/O意味着一直等待设备可访问再访问,非阻塞I/O意味着使用poll()来查询是否可访问,而异步通知则意味着设备通知应用程序自身 可访问。

  

    在linux中,异步通知是使用信号来实现的,而在linux,大概有30种信号,比如大家熟悉的ctrl+c的SIGINT信号,进程能够忽略或者捕获除过SIGSTOP和SIGKILL的全部信号,当信号背捕获以后,有相应的函数来处理它。

在linux中,使用signal()函数来捕获信号,它的函数运行如下:


  1.                 #include 
                   typedef void (*sighandler_t)(int);
                   sighandler_t signal(int signum, sighandler_t handler);
   第 一个参数就是指定的信号的值,而第二个参数便是此信号的信号处理函数,当为SIG_IGN,表示信号被忽略,当为SIG_DFL时,表示采用系统的默认方 式来处理该信号。当然,信号处理函数也可以自己定义。当signal()调用成功后,返回处理函数handler值,调用失败后返回SIG_ERR。


    为了使设备能够实现异步通知功能需要在驱动程序和应用程序分别作如下操作

驱动程序中涉及以下3项工作:
1. 支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID。
   不过此项工作已由内核完成,设备驱动无须处理。
2. 支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序中的fasync()函数将得以执行。
   驱动中应该实现fasync()函数。
3. 在设备资源可获得时,调用kill_fasync()函数激发相应的信号


应用程序要有如下代码:

fcntl(fd, F_SETOWN, getpid());  // 告诉内核,发给谁

Oflags = fcntl(fd, F_GETFL);   
fcntl(fd, F_SETFL, Oflags | FASYNC);  // 改变fasync标记,最终会调用到驱动的faync > fasync_helper:初始化/释放fasync_struct


    下面以tiny210按键驱动来实现异步通知的实例

按键驱动程序如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>

#include <mach/map.h>
#include <mach/gpio.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>

#define DEVICE_NAME		"buttons_poll"

struct button_desc{
	  int gpio;
	  int number;
	  char *name;
	  struct timer_list timer;
	
};

static struct button_desc buttons[]={
	{S5PV210_GPH2(0),0,"KEY0"},
	{S5PV210_GPH2(1),1,"KEY1"},
	{S5PV210_GPH2(2),2,"KEY2"},
	{S5PV210_GPH2(3),3,"KEY3"},
	{S5PV210_GPH2(4),4,"KEY4"},
	{S5PV210_GPH2(5),5,"KEY5"},
	{S5PV210_GPH2(6),6,"KEY6"},
	{S5PV210_GPH2(7),7,"KEY7"},
	
};

static volatile char key_values[]={
	'0','0','0','0','0','0','0','0'
};

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

static volatile int ev_press=0;
static struct fasync_struct *button_fasync; 

static void mini210_buttons_timer(unsigned long _data)
{
	struct button_desc *bdata=(struct button_desc*)_data;
	int down;
	int number;
	unsigned tmp;
	tmp = gpio_get_value(bdata->gpio);
	down =!tmp;
	
	printk("KEY%d:%08x\n",bdata->number,down);
	number=bdata->number;
	if (down!=(key_values[number]&1)){
		key_values[number]='0'+down;
		
		ev_press=1;
		wake_up_interruptible(&button_waitq);
		kill_fasync(&button_fasync, SIGIO, POLL_IN);  
	}
}


static irqreturn_t button_interrupt(int irq,void *dev_id)

{
	struct button_desc *bdata=(struct button_desc*)dev_id;
	mod_timer(&bdata->timer,jiffies+msecs_to_jiffies(40));
	return IRQ_HANDLED;
	
	
}

static int mini210_buttons_open(struct inode *inode,struct file *file)
{
	int irq;
	int i;
	int err=0;
        printk("open buttons fan");
	for (i=0;i<ARRAY_SIZE(buttons);i++)
	{
		if(!buttons[i].gpio)
			continue;
		setup_timer(&buttons[i].timer, mini210_buttons_timer,
				(unsigned long)&buttons[i]);
				irq = gpio_to_irq(buttons[i].gpio);
		err = request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_BOTH, 
				buttons[i].name, (void *)&buttons[i]);
		if (err)
			break;
		
	}
		if (err) 
	{
		i--;
		for (; i >= 0; i--) {
			if (!buttons[i].gpio)
				continue;

			irq = gpio_to_irq(buttons[i].gpio);
			disable_irq(irq);
			free_irq(irq, (void *)&buttons[i]);

			del_timer_sync(&buttons[i].timer);
		}

		return -EBUSY;
	
	
    }


}


static int mini210_buttons_close(struct inode *inode, struct file *file)
{
	int irq, i;

	for (i = 0; i < ARRAY_SIZE(buttons); i++) {
		if (!buttons[i].gpio)
			continue;

		irq = gpio_to_irq(buttons[i].gpio);
		free_irq(irq, (void *)&buttons[i]);

		del_timer_sync(&buttons[i].timer);
	}

	return 0;
}


static int mini210_buttons_read(struct file *filp, char __user *buff,
		size_t count, loff_t *offp)
{
	unsigned long err;

	if (!ev_press) {
		if (filp->f_flags & O_NONBLOCK)
			return -EAGAIN;
		else
			wait_event_interruptible(button_waitq, ev_press);
	}
       printk("read buttons fan");
	ev_press = 0;

	err = copy_to_user((void *)buff, (const void *)(&key_values),
			min(sizeof(key_values), count));

	return err ? -EFAULT : min(sizeof(key_values), count);
}


static unsigned int mini210_buttons_poll( struct file *file,
		struct poll_table_struct *wait)
{
	unsigned int mask = 0;

	poll_wait(file, &button_waitq, wait);
	if (ev_press)
		mask |= POLLIN | POLLRDNORM;

	return mask;
}

static int  mini210_buttons_fasync(int fd, struct file *filp, int on)  
{  
    return fasync_helper(fd, filp, on, &button_fasync);  
} 


static struct file_operations dev_fops = {
	.owner		= THIS_MODULE,
	.open		= mini210_buttons_open,
	.release	= mini210_buttons_close, 
	.read		= mini210_buttons_read,
	.poll		= mini210_buttons_poll,
	.fasync     = mini210_buttons_fasync, 
};

static struct miscdevice misc = {
	.minor		= MISC_DYNAMIC_MINOR,
	.name		= DEVICE_NAME,
	.fops		= &dev_fops,
};


static int __init button_dev_init(void)
{
	int ret;

	ret = misc_register(&misc);

	printk(DEVICE_NAME"\tinitialized\n");

	return ret;
}

static void __exit button_dev_exit(void)
{
	misc_deregister(&misc);
}

module_init(button_dev_init);
module_exit(button_dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");


应用程序

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <poll.h>  
#include <signal.h>  

int buttons_fd;
char buttons[8] = {'0', '0', '0', '0', '0', '0', '0', '0'};

void mysignal_fun(int signum)  
{  
        char current_buttons[8];
		int count_of_changed_key;
		int i;
		int ret;
		
       
		if (read(buttons_fd, current_buttons, sizeof current_buttons) != sizeof current_buttons) {
			perror("read buttons:");
			exit(1);
		}

		for (i = 0, count_of_changed_key = 0; i < sizeof buttons / sizeof buttons[0]; i++) {
			if (buttons[i] != current_buttons[i]) {
				buttons[i] = current_buttons[i];
				printf("%skey %d is %s", count_of_changed_key? ", ": "", i+1, buttons[i] == '0' ? "up" : "down");
				count_of_changed_key++;
			}
		}
		
		if (count_of_changed_key) {
			printf("\n");
		}
}  
  

int main(void)
{
	
	
    int flag;  
    signal(SIGIO,mysignal_fun); 
	
	buttons_fd = open("/dev/buttons_poll", 0);
	if (buttons_fd < 0) {
		perror("open device buttons");
		exit(1);
	}
     /* F_SETOWN:  Set the process ID 
     *  告诉内核,发给谁 
     */  
    fcntl(buttons_fd, F_SETOWN, getpid());  
  
    /*  F_GETFL :Read the file status flags 
     *  读出当前文件的状态 
     */  
    flag = fcntl(buttons_fd,F_GETFL);  
  
    /* F_SETFL: Set the file status flags to the value specified by arg 
     * int fcntl(int fd, int cmd, long arg); 
     * 修改当前文件的状态,添加异步通知功能 
     */  
    fcntl(buttons_fd,F_SETFL,flag | FASYNC);  
	
	 while(1)  
    {  
        /* 为了测试,主函数里,什么也不做 */  
        sleep(1000);  
    }  
	close(buttons_fd);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值