概述
按键驱动:异步通知
4种读按键方式对比:
查询:非常耗费CPU资源
中断:会休眠,若一直没有按键动作,将一直休眠
Poll:可以指定超时时间,有按键动作立即唤醒,或超时后唤醒
异步通知:驱动程序主动通知应用程序可以读数据了
前3种都是应用程序读的时候资源并不一定就绪,异步通知是驱动程序通知应用程序去读,此时资源已经就绪
思路指导:
1. 应用程序:注册信号处理函数,接收到信号时会调用此函数
2. 谁发?驱动程序
3. 发给谁?应用程序,因此app需要告诉驱动自己的pid
4. 怎么发?调用kill_fasync()
编程要点:
驱动程序:
1. 支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID。不过此项工作已由内核完成,驱动程序无需处理。
2. 支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序中的file_operations->fasync()得以执行。
3. 在设备资源可获得时,调用kill_fasync()函数激发相应的信号。
应用程序:
signal(SIGIO, my_signal_fun); // 设置接收到SIGIO信号后的回调函数
// SIGIO表示有数据读或有空间写了
// 在回调函数中去执行read, write
fcntl(fd, F_SETOWN, getpid()); // 告诉内核发给谁
oflags = fcntl(fd, F_GETFL); // 若要修改标志,先读出原始的
fcntl(fd, F_SETFL, oflags | FASYNC); // 改变fasync标记,最终会调用到驱动的fops->fasync()
源码
驱动:
/*
* 引脚:PI0,1,2
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/leds.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/module.h>
#include <linux/pinctrl/consumer.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <asm/signal.h>
struct pin_desc{
int pin;
int val;
};
/*
* 按下时,返回:0x81, 0x82, 0x83
*
*/
static struct pin_desc pins_desc[3] = {
{NUC970_PI0, 0x1},
{NUC970_PI1, 0x2},
{NUC970_PI2, 0x3},
};
static unsigned char val;
static DECLARE_WAIT_QUEUE_HEAD(buttons_waitq);
int evpress = 0;
struct fasync_struct *buttons_async;
static irqreturn_t buttons_handler(int irq, void *dev_id)
{
struct pin_desc *pin = (struct pin_desc *)dev_id;
//int res;
//res = gpio_get_value(pin->pin);
val = 0x80 | pin->val;
evpress = 1;
wake_up_interruptible(&buttons_waitq);
kill_fasync(&buttons_async, SIGIO, POLL_IN); // 发送信号
return IRQ_HANDLED;
}
static int buttons_open(struct inode *inode, struct file *filp)
{
// request_irq会自动设置引脚,此处不再配置
request_irq(gpio_to_irq(pins_desc[0].pin), buttons_handler, IRQF_TRIGGER_FALLING, "S1", &pins_desc[0]);
request_irq(gpio_to_irq(pins_desc[1].pin), buttons_handler, IRQF_TRIGGER_FALLING, "S2", &pins_desc[1]);
request_irq(gpio_to_irq(pins_desc[2].pin), buttons_handler, IRQF_TRIGGER_FALLING, "S3", &pins_desc[2]);
return 0;
}
static ssize_t buttons_read (struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
if (count != 1)
{
return -EINVAL;
}
wait_event_interruptible(buttons_waitq, evpress);
evpress = 0;
copy_to_user(buf, &val, 1);
return 1;
}
int buttons_release (struct inode *inode, struct file *filp)
{
free_irq(gpio_to_irq(pins_desc[0].pin), &pins_desc[0]);
free_irq(gpio_to_irq(pins_desc[1].pin), &pins_desc[1]);
free_irq(gpio_to_irq(pins_desc[2].pin), &pins_desc[2]);
return 0;
}
static unsigned int buttons_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
poll_wait(file, &buttons_waitq, wait); // 不会立即休眠,只是把进程挂到buttons_waitq队列
if (evpress)
{
mask = POLLIN | POLLRDNORM;
}
return mask;
}
// 每当应用程序修改FAYNC标记时,此程序就会被调用
static int buttons_fasync(int fd, struct file *filp, int on)
{
return fasync_helper(fd, filp, on, &buttons_async); // 此函数会初始化buttons_async
}
static struct file_operations buttons_fops = {
.owner = THIS_MODULE,
.open = buttons_open,
.read = buttons_read,
.release = buttons_release,
.poll = buttons_poll,
.fasync = buttons_fasync,
};
static int major;
static struct class *buttons_class;
static struct device *button_device;
static int buttons_init(void)
{
major = register_chrdev(0, "buttons", &buttons_fops);
buttons_class = class_create(THIS_MODULE, "buttons");
button_device = device_create(buttons_class, NULL, MKDEV(major, 0), NULL, "buttons");
return 0;
}
static void buttons_exit(void)
{
device_destroy(buttons_class, MKDEV(major, 0));
class_destroy(buttons_class);
unregister_chrdev(major, "buttons");
}
module_init(buttons_init);
module_exit(buttons_exit);
MODULE_LICENSE("GPL");
测试程序:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <fcntl.h>
int fd;
static void my_signal_fun(int sig)
{
unsigned char keyval;
read(fd, &keyval, 1);
printf("keyval = 0x%xn", keyval);
}
int main(void)
{
int oflags;
fd = open("/dev/buttons", O_RDONLY);
if (fd < 0)
{
printf("Can't open /dev/buttonsn");
return -1;
}
signal(SIGIO, my_signal_fun);
fcntl(fd, F_SETOWN, getpid()); // 告诉驱动发给谁
oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, oflags | FASYNC);
while (1)
{
sleep(1000);
}
close(fd);
return 0;
}
最后
以上就是光亮导师为你收集整理的一、字符设备驱动4-异步通知按键驱动按键驱动:异步通知源码的全部内容,希望文章能够帮你解决一、字符设备驱动4-异步通知按键驱动按键驱动:异步通知源码所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复