【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
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");