在(十四)的等待队列中,如果没有按键中断,应用程序就始终处于休眠状态,如果应用程序中,既不想占用太多的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 */
};
成员 | 描述 |
fd | open字符设备驱动返回的fd句柄 |
events | 请求的事件 |
revents | 返回的事件 |
2.2 定义超时时间 'timeout'
2.3 调用 'poll' 函数
poll(&fds, nfds, timeout)
参数 | 描述 |
&fds | pollfd定义的结构体指针 |
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秒钟会打印超时信息,当有按键按下时,输出按键的值