一、字符设备驱动4-异步通知按键驱动

按键驱动:异步通知

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%x\n", keyval);
}

int main(void)
{
	int oflags;

	fd = open("/dev/buttons", O_RDONLY);
	if (fd < 0)
	{
		printf("Can't open /dev/buttons\n");
		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;
}


猜你喜欢

转载自blog.csdn.net/zhen7620/article/details/80402298