自学linux驱动从入门到放弃(十五)poll的使用

        在(十四)的等待队列中,如果没有按键中断,应用程序就始终处于休眠状态,如果应用程序中,既不想占用太多的CPU资源,又要去处理其他的事情,那么就需要用到 "poll"。

1. linux驱动中 ‘poll’ 需要做哪些事情?

头文件:

#include <linux/poll.h>

1.1 在Driver的Poll中调用poll_wait,把线程挂入队列

        上文中,创建了一个等待队列,在Driver的read中休眠,在中断函数中唤醒,那么使用poll_wait就是将线程挂入这个队列。

static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)

1.2 返回event状态(POLLIN | POLLRDNORM | POLLOUT ...)

2. app中需要做什么事情

头文件:

#include <poll.h>

2.1 创建 'pollfd' 结构体并填充成员

 struct pollfd {
               int   fd;         /* file descriptor */
               short events;     /* requested events */
               short revents;    /* returned events */
           };
成员描述
fdopen字符设备驱动返回的fd句柄
events请求的事件
revents返回的事件

2.2 定义超时时间 'timeout'

2.3 调用 'poll' 函数

poll(&fds, nfds, timeout)
参数描述
&fdspollfd定义的结构体指针
nfds监控的文件个数
timeout超时时间(ms)
返回值成功:1 超时:0 错误:-1

3.代码实现

3.1 Driver

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>

#define RING_BUF_LEN	64
#define NEXT_POS(x)	((x+1)%RING_BUF_LEN)
static int g_keys[RING_BUF_LEN];
static int r,w;

static int is_buffer_empty(void)
{
	return (w == r);
}

static int is_buffer_full(void)
{
	return (r == NEXT_POS(w));
}

static void ring_key_put(int x)
{
	if( ! is_buffer_full())
	{
		g_keys[w] = x;
		w = NEXT_POS(w);
	}
}

static int ring_key_pop(void)
{
	int key = 0;
	if(! is_buffer_empty())
	{
		key = g_keys[r];
		r = NEXT_POS(r);
	}
	return key;
}


static DECLARE_WAIT_QUEUE_HEAD(q_key_wait);
int key_val = 0;

int inputPara=0;

int dev_major;
int dev_minor=0;
char *chardev_device_name="aloncharDevice";	//设备驱动名字
struct class *class;
char *chardev_class_name="aloncharClass";	//类名字
char *charDev_node_name="aloncharTest";		//设备节点名字
struct gpio_desc *gpio_res =NULL;
int irq = -1;

irqreturn_t key_fun(int irq,void *args)
{
	printk(KERN_ERR "test fun is action!!\n");
	key_val = gpiod_get_raw_value(gpio_res);
	printk(KERN_ERR "kernel: key value is %x \n",key_val);
	ring_key_put(key_val);
	
	wake_up_interruptible(&q_key_wait);
	return IRQ_HANDLED;
}


module_param(inputPara, int, S_IRUSR);		//insmod的时候传入的参数,参数名字inputPara,类型int,S_IRUSR是权限

/*
static volatile unsigned int *GPIO8_CLKGATE_CON;
static volatile unsigned int *GPIO_PORT_DR;
static volatile unsigned int *GPIO_PORT_DDR;
static volatile unsigned int *GRF_GPIO8A_IOMUX;
*/


int charDev_open (struct inode *inode, struct file *file){

/*
	*led_reg_vir_addr[GPIO8_CLKGATE_CON] = (1<<(16+8))|(0<<8);	//31:16 write mask :8 gpio8 clk gate,0:enable
	*led_reg_vir_addr[GRF_GPIO8A_IOMUX] = (1<<(16+4))|(~(1<<4));//31:16 write mask :4 0:fuc gpio
	*led_reg_vir_addr[GPIO_PORT_DDR] |= (1<<2);	//gpio8_A2 set output
*/
	printk("open charTest node OK!\n");


	return 0;
}

ssize_t charDev_read (struct file *file, char __user *buf, size_t size, loff_t *ppos){

	int err;
	wait_event_interruptible(q_key_wait, (!is_buffer_empty()));
	key_val = ring_key_pop();
	err = copy_to_user(buf,&key_val,1);
	printk(KERN_ERR "KEY VAL is %d \n",key_val);
	key_val = 0;
	return 0;
}

ssize_t charDev_write (struct file *file, const char __user *buf, size_t size, loff_t *ppos){


	return 0;
}


static unsigned int charDev_poll(struct file *fp, poll_table * wait)
{

	poll_wait(fp, &q_key_wait, wait);    //将线程放入队列
	return is_buffer_empty() ? 0 : POLLIN | POLLRDNORM;    //判断是否有数据,有则返回POLLIN|POLLRDNORM
}



static const struct file_operations f_op = {	//file_operations结构体
	.open = charDev_open,
	.read = charDev_read,
	.write = charDev_write,
	.poll = charDev_poll,    //DRIVER的poll函数
	.owner = THIS_MODULE,
}; 

int led_probe(struct platform_device *pdev){


	int ret;
	printk(KERN_ERR "dts match driver!!\n");

	gpio_res = gpiod_get_index(&pdev->dev,"key",0,GPIOD_OUT_LOW);	//获取名为key的gpio属性
	if(IS_ERR(gpio_res))
	{
		printk(KERN_ERR "gpiod_get_index is error!!\n");
		return (PTR_ERR(gpio_res));
	}
	printk(KERN_ERR "get gpio is ok!!\n");
//	ret = gpiod_direction_output(gpio_res,0);
	ret = gpiod_direction_input(gpio_res);		//将引脚设置为输入
	if(ret < 0)
	{
		printk(KERN_ERR "gpio_direction_input is error!!\n");
		return -1;		
	}
//	irq = gpiod_to_irq(gpio_res);				//将gpio转换为中断号
//	irq = irq_of_parse_and_map(pdev->dev.of_node,0);	//通过设备树结点获取中断信息
	irq = of_irq_get(pdev->dev.of_node, 0);

	printk(KERN_ERR "irq is %d!!\n",irq);
	ret = request_irq(irq, key_fun, IRQF_TRIGGER_RISING, "key_interrupt", NULL);	//申请中断号
	
	if(ret < 0)
	{
		printk(KERN_ERR "request_irq is failed!!\n");
	}
	
	dev_major = register_chrdev(0,chardev_device_name,&f_op);	//注册设备号,f_op结构体
	if(dev_major < 0)
	{
		printk("can not regist char device!\n");
		return dev_major;
	}
	printk(KERN_ERR "dev_major = %d,chardev device name is %s\n",dev_major,chardev_device_name);
	class = class_create(THIS_MODULE, chardev_class_name);		//注册类
	if(IS_ERR(class))
	{
		printk("can not create charDev class!");
		unregister_chrdev(dev_major,chardev_device_name);
		return PTR_ERR(class);
			
	}
	printk(KERN_ERR "chardev class name is %s\n",chardev_class_name);
	device_create(class, NULL, MKDEV(dev_major,dev_minor), NULL, charDev_node_name);	//注册设备节点
	printk(KERN_ERR "chardev node create ok!\n");
	



	
	return 0;
}

int led_remove(struct platform_device *pdev){


	return 0;
}

const struct of_device_id alon_of_match_table[] = {
	{.compatible = "key_test"},
	{}
};

struct platform_driver led_driver = {
	.probe = led_probe,
	.remove = led_remove,
	.driver = {
		.owner = THIS_MODULE,
		.name = "alon_charDevPlt_test",
		.of_match_table = alon_of_match_table
	}
};


static int charDev_init(void)			//init函数,MODULE_INIT的时候会进行初始化,当前框架里面
{										//注册设备号,注册类,注册设备节点都在这里
	


	int ret = 0;

	ret = platform_driver_register(&led_driver);
	if(ret < 0){
		printk(KERN_ERR "platform driver register failed \n");
		return ret;
	}
	printk(KERN_ERR "insmod platform driver\n");


	if(inputPara)			//这里是测试参数是否传入,执行insmod devDrv.ko inputPara=整数(等号前后不能有空格)。
	{
		printk(KERN_ERR "input Param is %d\n",inputPara);
	}
	printk(KERN_ERR "registers platform driver\n");	


	return 0;
}

static void charDev_exit(void)
{


	printk(KERN_ERR "remove platform driver\n");
	if(IS_ERR(gpio_res)){
		printk(KERN_ERR "not get gpio info\n");
	}
	else{
		gpiod_put(gpio_res);
	}
	if(irq >= 0)
		free_irq(irq,NULL);
		
	
	printk(KERN_ERR "gpio_free\n");
	device_destroy(class, MKDEV(dev_major,dev_minor));
	printk(KERN_ERR "remove chardev node!\n");
	class_destroy(class);
	
	printk(KERN_ERR "remove chardev class!\n");
	unregister_chrdev(dev_major,chardev_device_name);
	printk(KERN_ERR "remove chardev device!\n");

	platform_driver_unregister(&led_driver);
}



module_init(charDev_init);
module_exit(charDev_exit);
MODULE_LICENSE("GPL");

3.2 App

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

int main(int argc, const char ** argv)
{
	int fd;
	int val;
	int ret;


	struct pollfd fds[1];    //定义pollfd 结构体
	int timeout_ms = 2000;    //定义超时时间
	
	fd = open("/dev/aloncharTest",O_RDWR);
	

	if(fd < 0)
	{
		printf("APP can not open node!\n");
		return 0;
	}
	printf("open /dev/aloncharTest ok!\n");

	fds[0].fd = fd;        //填充结构体成员
	fds[0].events = POLLIN;
	
	while(1)
	{
		ret = poll(fds,1,timeout_ms);    //poll函数
		if((ret == 1)&&(fds[0].revents & POLLIN))    //poll执行成功并且返回的事件为POLLIN
		{
			read(fd,&val,1);        //读取数据
			printf("key value is %d \n",val);
		}
		else
		{
			printf("app timeout! \n",val);
		}
	}
	
	close(fd);

	return 0;
}

4.执行结果

可以看到2秒钟会打印超时信息,当有按键按下时,输出按键的值

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值