Linux驱动开发之按键驱动(2)--------- 多路复用和异步信号通知模型


一,IO多路复用



1.IO多路复用模型


在这里插入图片描述


2.poll的应用


1.poll函数

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

头文件包含】:#include <poll.h>
参数】:
参数1:表示多个文件描述符的集合,用于描述fd的信息

    struct pollfd {
               int   fd;         /* 文件描述符*/
               short events;     /* 希望监控fd的什么事件 */
               short revents;    /* 结果描述,表示当前的fd是否有读,写,出错 */
           };
           
POLLIN     读
POLLOUT    写
POLLERR    出错

参数2 :被监控的fd的个数

参数3:监控的时间
正 ------>表示监控多少微秒
负 ------>表示无限时间去监控
0 ------>表示不等待,非阻塞

返回值】:
大于0 ------>表示fd中有数据
负数 ------>表示出错
0 ------>表示时间到

2.poll应用编程

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>

#define KEY_ENTER 28
struct key_event{
	int code;  //表示按键的类型
	int value; //表示按键的按下还是抬起
};

static int fd;
static struct key_event event;

int main(int argc, char const *argv[])
{
	
	char in_buf[128];
	int ret;
	int fd = open("/dev/key0",O_RDWR);
	if(fd < 0)
	{
		perror("open");
		exit(-1);
	}
     //实现IO多路复用
	//监控多个设备
	struct pollfd pfd[2];
	pfd[0].fd = fd;  //监控按键设备
	pfd[0].events = POLLIN;  //读

	pfd[1].fd = 0;  //监控标准输入
	pfd[1].events = POLLIN;  //读

	while(1)
	{		
		ret = poll(pfd,2,-1);
		if(ret > 0)
		{
			printf("ret = %d\n",ret);
			//表示两个fd中至少有一个发生了数据可读
			if(pfd[0].revents & POLLIN)
			{
				//可以去读数据
				read(pfd[0].fd,&event,sizeof(struct key_event));
				if(event.code == KEY_ENTER)
				{
					if(event.value)
					{
						printf("APP_KEY pressed\n");
					}
					else
				   {
				   		printf("APP_KEY up\n");
				   }
				}
			}
			if(pfd[1].revents & POLLIN)
			{
				fgets(in_buf,128,stdin);
				printf("in_buf = %s\n",in_buf);
			}
		}
		else
		{
			perror("poll");
			exit(-1);
		}
	}

	close(pfd[0].fd);
	return 0;
}

2.poll驱动接口的实现


//描述按键的信息
struct key_desc{
	unsigned int dev_major;
	struct class* cls;
	struct device* dev;
	int irqno;
	void *reg_base;
	struct key_event event;
	wait_queue_head_t wq_head;
	int key_state;   //表示是否有数据
	struct fasync_struct * fasync;
	struct tasklet_struct mytasklet;
};


struct key_desc* key_dev;
const struct file_operations key_fops = {
	.open = key_drv_open,
	.read = key_drv_read,
	.write = key_drv_write,
	.release = key_drv_close,
	.poll = key_dev_poll,          //增加poll接口
	.fasync = key_dev_fasync,
};

//实现驱动里的poll接口
unsigned int key_dev_poll (struct file *filp, struct poll_table_struct *pts)
{
	printk("----%s----\n",__FUNCTION__);
	//返回一个mask值
	unsigned int mask;
	//调用poll_wait将当前等待队列注册到系统中
	poll_wait(filp, &key_dev->wq_head,pts);
	// 1,没有数据的时候返回一个0
	if(!key_dev->key_state)
	{
		mask = 0;
	}
	// 2,当有数据的时候返回一个POLLIN
	if(key_dev->key_state)
	{
		mask |= POLLIN;
	}
	return mask;
}


二,异步信号通知



1.应用-------处理信号,主要是读写数据


  • 1,设置信号处理方法
	signal(SIGIO,catch_signale);
  • 2,将当前进程设置成SIGIO的属主进程
	fcntl(fd,F_SETOWN,getpid());
  • 3,把IO模式设置成异步模式
	int flags = fcntl(fd,F_GETFL);
	fcntl(fd,F_GETFL,flags | FASYNC);

具体代码

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <signal.h>

#define KEY_ENTER 28
struct key_event{
	int code;  //表示按键的类型
	int value; //表示按键的按下还是抬起
};

static int fd;
static struct key_event event;

void catch_signale(int signo)
{
	printf("we got signal SIGN\n");
	//读取数据
	if(signo == SIGIO)
	{
		read(fd,&event,sizeof(struct key_event));
		if(event.code == KEY_ENTER)
		{
			if(event.value)
			{
				printf("APP_KEY pressed\n");
			}
			else
			{
				printf("APP_KEY up\n");
			}			
		}
	}
}

int main(int argc, char const *argv[])
{
	
	char in_buf[128];
	int ret;

	int fd = open("/dev/key0",O_RDWR);
	if(fd < 0)
	{
		perror("open");
		exit(-1);
	}
	
	//1,设置信号处理方法
	signal(SIGIO,catch_signale);

	//2,将当前进程设置成SIGIO的属主进程
	fcntl(fd,F_SETOWN,getpid());

	//3,把IO模式设置成异步模式
	int flags = fcntl(fd,F_GETFL);
	fcntl(fd,F_GETFL,flags | FASYNC);

	while(1)
	{
		//可以做其他事情
		printf("I am waiting.\n");
		sleep(1);
	}
	close(fd);
	return 0;
}

2.驱动--------发送信号


  • 需要和进程关联-------->记录信号该发送给谁,即要在驱动中实现一个fasync接口

//描述按键的信息
struct key_desc{
	unsigned int dev_major;
	struct class* cls;
	struct device* dev;
	int irqno;
	void *reg_base;
	struct key_event event;
	wait_queue_head_t wq_head;
	int key_state;   //表示是否有数据
	struct fasync_struct * fasync;
	struct tasklet_struct mytasklet;
};

struct key_desc* key_dev;
int key_dev_fasync (int fd, struct file *filp, int on)
{
	//只需要调用一个函数记录信号该发送给谁
	return fasync_helper(fd, filp,on, &key_dev->fasync);
}


const struct file_operations key_fops = {
	.open = key_drv_open,
	.read = key_drv_read,
	.write = key_drv_write,
	.release = key_drv_close,
	.poll = key_dev_poll,
	.fasync = key_dev_fasync,
};
  • 在某个特定的时候去发送信号----------->有数据来的时候,即触发中断的函数里
irqreturn_t key_irq_handler (int irq, void * dev_id)
{
	printk("------%s----\n",__FUNCTION__);
	int value = readl(key_dev->reg_base) & (1<<2);
	if(value)
	{
		//按键抬起
		printk("key up\n");
		key_dev->event.code = KEY_ENTER;
		key_dev->event.value = 0;
	}
	else
	{
		//按键按下
		printk("key press\n");
		key_dev->event.code = KEY_ENTER;
		key_dev->event.value = 1;

	}
	//发送信号
	kill_fasync(&key_dev->fasync, SIGIO, POLLIN);
	return IRQ_HANDLED;
}

继续参考下一篇博客:Linux驱动开发之按键驱动(3)--------- 中断下半部的实现方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值