Linux中的异步通知

【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) 

Linux中的异步通知

  当我们的应用程序需要使用硬件设备时, 需要一直查询该硬件设备是否准备就绪, 这是一个不断查询的过程, 若一直未就绪就一直查询, 非常消耗资源.
  而我们Linux的异步通知就是为了解决这一个问题. 当设备准备就绪时, 主动告诉应用, 从而节省不断查询而导致的资源浪费.

  应用程序工作在Linux的用户空间, 而驱动程序工作在Linux的内核空间中, 两者是不能直接通信的. 可以在驱动层向对应的应用发送一个信号, 应用接收到信号后调用相对应的信号处理函数, 从而达到异步通知的效果. 实现步骤如下:

1. 应用层

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

int fd = -1;

void sig_handler(int signum)	// 这是一个信号处理函数, 接收到该信号表示设备就绪
{
	int value;
	read(fd, &value, sizeof(value));	// 设备就绪即可执行你想要的操作, 我这里是读设备.
	printf("sig_handler() value: %d\n", value);
}

int main(int argc, char **argv)
{
	int value, ret;
	int flags;

	signal(SIGIO, sig_handler);	// 设置信号处理函数, 当应用(该进程)接收到SIGIO信号时, sig_handler()函数将会被调用.

	fd = open("/dev/yangbkDevice", O_RDWR);	// 获得对应设备的文件描述符
	if(fd < 0) {
		perror("open() fail:");
		return -1;
	}

	fcntl(fd, F_SETOWN, getpid());	// 绑定文件描述符所在的进程ID号, 驱动通过pid得知该向哪个进程发送信号.
	flags = fcntl(fd, F_GETFL); 	// 获取文件描述符的状态标记, 即该文件描述符支持哪些操作
	fcntl(fd, F_SETFL, flags | FASYNC);	// 设置该文件描述符支持异步通知, 当I/O可用的时候, 允许SIGIO信号发送到进程组

	while(1) {
		sleep(1);	// 不执行任何其他操作, 坐等驱动层给我们发送SIGIO信号
	}

	close(fd);
	return 0;
}

需要注意:
  在修改文件描述符标志或文件状态标志时必须谨慎, 先要取得现在的标志值, 然后按照希望修改它, 最后设置新标志值. 不能只是执行F_SETFL命令, 这样会关闭以前设置的标志位.

2. 驱动层(完整代码在文章最后贴出)

2.1 在file_operations结构体中添加.fasync成员:
static struct file_operations btn_fops = {
	......
	.fasync = btn_fasync,
};
2.2 实现在file_operations结构体中添加.fasync成员: btn_fasync
static struct fasync_struct *button_async;

static int btn_fasync(int fd, struct file *pfile, int on)
{
	printk("btn_fasync()\n");

	//fasync_helper作用就是初始化button_async, 包括分配内存和设置属性
	return fasync_helper (fd, pfile, on, &button_async);
}
2.3 当设备就绪时, 发送信号:
// 发送SIGIO信号的同时, 把fasync_helper初始化的东西free掉.
kill_fasync (&button_async, SIGIO, POLL_IN);

3. fasync_helper 与 kill_fasync

  kill_fasync及fasync_helper用于异步通知中

  fasync_helper在初始化button_async时, 将应用相关的进程信息绑定在该结构体内, 当驱动层调用kill_fasync发出信号时, 对应的button_async中的进程信息将被找到并成功发送出信号.
  也就是说, 哪个进程调用这个fasync_helper就向哪个进程发送信号(冤有头债有主~).

4. 最后附上完整的驱动层代码:

#include <linux/init.h>
#include <linux/module.h>

#include <linux/of.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <asm/uaccess.h>

#include <linux/cdev.h>
#include <linux/device.h>

#include <linux/interrupt.h>

#include <linux/poll.h>

static dev_t dev_num = 0;
static struct cdev *cdevice = NULL;

static struct class *sys_class = NULL;
static struct device *class_device = NULL;

struct pin_desc{
	volatile unsigned int *gpio_con;
	volatile unsigned int *gpio_dat;
	int irq;
};

static int gkey_val;
struct pin_desc pins_desc[1] = {
	{NULL, NULL}, 
};

/* 中断事件标志, 中断服务程序将它置1,btn_read将它清0 */
static volatile int ev_press = 0;
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

/* 异步通知 */
static struct fasync_struct *button_async;

static irqreturn_t btn_handler(int irq, void *dev_id)
{
	struct pin_desc *pindesc = (struct pin_desc *)dev_id;
	
	gkey_val = *(pindesc->gpio_dat);
	ev_press = 1;	/* 表示中断发生了 */
	wake_up_interruptible(&button_waitq);	/* 唤醒休眠的进程 */

	kill_fasync (&button_async, SIGIO, POLL_IN);

	return IRQ_RETVAL(IRQ_HANDLED);;
}

int btn_open(struct inode *pinode, struct file *pfile)
{
	int ret = -1;

	// input mode
	*(pins_desc[0].gpio_con + 100) &= 0<<1;

	// request irq
	ret = request_irq(pins_desc[0].irq, btn_handler, IRQF_TRIGGER_HIGH | IRQF_SHARED, 
						"ybk_btn", &pins_desc[0]);
	printk("ret = %d, pins_desc[0].irq = %d\n", ret, pins_desc[0].irq);
	
	return 0;
}

int btn_close(struct inode *pinode, struct file *pfile)
{
	free_irq(pins_desc[0].irq, &pins_desc[0]);
	return 0;
}

ssize_t btn_read(struct file *pfile, char __user *userbuf, size_t size, loff_t *loff)
{
	/**
	 * 轮询方式的read()
	 * 
	 * int value = *(pins_desc[0].gpio_dat);
	 * copy_to_user(userbuf, &value, sizeof(value));
	 */ 
	
	if(size != sizeof(int))
		return -EINVAL;

	/* 如果没有按键动作, 休眠 */
	wait_event_interruptible(button_waitq, ev_press);

	/* 如果有按键动作, 返回键值 */
	copy_to_user(userbuf, &gkey_val, sizeof(gkey_val));
	ev_press = 0;

	return size;
}

static unsigned int btn_poll(struct file *pfile, struct poll_table_struct *wait)
{
	unsigned int mask = 0;
	poll_wait(pfile, &button_waitq, wait); // 不会立即休眠

	if (ev_press)
		mask |= POLLIN | POLLRDNORM;

	return mask;
}

static int btn_fasync(int fd, struct file *pfile, int on)
{
	printk("btn_fasync()\n");
	return fasync_helper (fd, pfile, on, &button_async);//fasync_helper作用就是初始化fasync, 包括分配内存和设置属性
}

static struct file_operations btn_fops = {
	.owner = THIS_MODULE,
	.open  = btn_open,
	.read  = btn_read,
	.release = btn_close,
	.poll = btn_poll,
	.fasync = btn_fasync,
};

int btn_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct device_node *dp_node = dev->of_node;
	struct resource *res = NULL;
	int i, reg[2];
	
	for(i=0; i<sizeof(pins_desc)/sizeof(pins_desc[0]); i++) {
		res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
		if(res) {
			pins_desc[i].irq = res->start;
		} else {
			printk("get irq failed!\n");
			return -EINVAL;
		}
		
		of_property_read_u32_array(dp_node, "reg", reg, sizeof(reg)/sizeof(reg[0]));

		pins_desc[i].gpio_con = (volatile unsigned int *)ioremap(reg[0], reg[1]);
		pins_desc[i].gpio_dat = pins_desc[i].gpio_con + 2;
	}

	printk("btn_probe, found btn\n");
	cdevice = cdev_alloc();
	cdev_init(cdevice, &btn_fops);

	alloc_chrdev_region(&dev_num, 0, 1, "yangbkBtn");
	cdev_add(cdevice, dev_num, 1);

	sys_class = class_create(THIS_MODULE, "yangbkClass");
	class_device = device_create(sys_class, NULL, dev_num, NULL, "yangbkDevice");
	
	return 0;
}

int btn_remove(struct platform_device *pdev)
{
	printk("btn_remove, remove btn\n");

	device_destroy(sys_class, dev_num);
	class_destroy(sys_class);

	unregister_chrdev_region(dev_num, 1);
	
	cdev_del(cdevice);

	iounmap(pins_desc[0].gpio_con);

	return 0;
}

static struct of_device_id	of_match_table = {
	.compatible = "ybk_btn",
	.data = NULL,
};

struct platform_driver btn_drv = {
	.probe		= btn_probe,
	.remove		= btn_remove,
	.driver		= {
		.name	= "idle",
		.of_match_table = &of_match_table,
	}
};


static int __init btn_drv_init(void)
{
	platform_driver_register(&btn_drv);
	return 0;
}

static void __exit btn_drv_exit(void)
{
	platform_driver_unregister(&btn_drv);
}

module_init(btn_drv_init);
module_exit(btn_drv_exit);

MODULE_AUTHOR("yangbkGit");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("platform driver model.");
MODULE_ALIAS("model");
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安河桥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值