linux异步通知:
应用程序需要完成如下三个步骤:
1)signal(SIGIO, sig_handler);
调用signal函数,让指定的信号SIGIO与处理函数sig_handler对应。
2)fcntl(fd, F_SET_OWNER, getpid());
指定一个进程作为文件的“属主(filp->owner)”,这样内核才知道信号要发给哪个进程。
3)f_flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, f_flags | FASYNC);
在设备文件中添加FASYNC标志,驱动中就会调用将要实现的test_fasync函数。
三个步骤执行后,一旦有信号产生,相应的进程就会收到。
内核需要完成下面四个步骤:
1)定义结构体fasync_struct。
struct fasync_struct *async_queue;
2)实现test_fasync,把函数fasync_helper将fd,filp和定义的结构体传给内核。
int test_fasync (int fd, struct file *filp, int mode)
{
struct _test_t *dev = filp->private_data;
return fasync_helper(fd, filp, mode, &dev->async_queue);
}
3)当设备可写时,调用函数kill_fasync发送信号SIGIO给内核。
if (dev->async_queue){
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
}
4)完成ops
struct file_operations test_fops = {
.open = test_open,
.release = test_close,
.write = test_write,
.read = test_read,
.poll = test_poll,
.fasync = test_fasync
补充:
在文件关闭时,即在设备驱动的release()函数中,应调用设备驱动的fasync()函数将文件从异步通知的列表
中删除
int xxx_release (struct inode *inode, struct file *filp)
{
struct xxx_dev *dev = filp->private_data;
xxx_fasync(-1, filp, 0);
...
return 0;
}
基于QT210和linux3.4.2设置如下:
配置内核:
参考原理图:
编译内核代码:
- #include <linux/types.h>
- #include <linux/module.h>
- #include <linux/cdev.h>
- #include <linux/fs.h>
- #include <linux/device.h>
- #include <linux/gpio.h>
- #include <linux/irq.h>
- #include <linux/interrupt.h>
- #include <linux/sched.h>
- #include <linux/wait.h>
- #include <linux/uaccess.h>
- #include <mach/gpio.h>
- #include <asm/uaccess.h>
- #include <asm/irq.h>
- #include <asm/io.h>
- static dev_t devno;
- static struct cdev cdev;
- static struct class* buttons_class;
- static struct device* buttons_device;
- static wait_queue_head_t button_waitq;
- static struct timer_list buttons_timer;
- static volatile int pressed = 0;
- static unsigned int key_val;
- static struct fasync_struct *button_async;
- struct key_desc{
- unsigned int pin;
- unsigned char value;
- };
- static struct key_desc *irq_pd;
- static volatile unsigned long *gph3con;
- static volatile unsigned long *gph3dat;
- static struct key_desc key_descs[8] = {
- [0] = {
- .pin = S5PV210_GPH2(3),
- .value = 0x00,
- },
- [1] = {
- .pin = S5PV210_GPH2(4),
- .value = 0x01,
- },
- [2] = {
- .pin = S5PV210_GPH2(5),
- .value = 0x02,
- },
- [3] = {
- .pin = S5PV210_GPH2(6),
- .value = 0x03,
- },
- [4] = {
- .pin = S5PV210_GPH2(7),
- .value = 0x04,
- },
- };
- static void buttons_timer_function(unsigned long data)
- {
- struct key_desc * pindesc = irq_pd;
- unsigned int pinval;
- if (!pindesc)
- return;
- pinval = gpio_get_value(pindesc->pin);
- if (pinval)
- {
- /* 松开 */
- key_val = 0x80 | pindesc->value;
- }
- else
- {
- /* 按下 */
- key_val = pindesc->value;
- }
- pressed = 1;
- wake_up_interruptible(&button_waitq);
- kill_fasync (&button_async, SIGIO, POLL_IN);
- }
- static irqreturn_t buttons_irq(int irq, void *dev_id){
- printk("buttons_irq happen\n");
- /* 10ms后启动定时器 */
- irq_pd = (struct key_desc *)dev_id;
- mod_timer(&buttons_timer, jiffies+HZ/100);
- return IRQ_RETVAL(IRQ_HANDLED);
- }
- static int buttons_open(struct inode *inode, struct file *file){
- int ret;
- ret = request_irq(IRQ_EINT(19), buttons_irq, IRQ_TYPE_EDGE_BOTH, "key1", &key_descs[0]);
- if(ret)
- return ret;
- ret = request_irq(IRQ_EINT(20), buttons_irq, IRQ_TYPE_EDGE_BOTH, "key2", &key_descs[1]);
- if(ret)
- return ret;
- ret = request_irq(IRQ_EINT(21), buttons_irq, IRQ_TYPE_EDGE_BOTH, "key3", &key_descs[2]);
- if(ret)
- return ret;
- ret = request_irq(IRQ_EINT(22), buttons_irq, IRQ_TYPE_EDGE_BOTH, "key4", &key_descs[3]);
- if(ret)
- return ret;
- ret = request_irq(IRQ_EINT(23), buttons_irq, IRQ_TYPE_EDGE_BOTH, "key5", &key_descs[4]);
- if(ret)
- return ret;
- return 0;
- }
- static ssize_t buttons_read(struct file * file, char __user *data, size_t count, loff_t *loff){
- if (count != 1)
- return -EINVAL;
- if (file->f_flags & O_NONBLOCK)
- {
- if (!pressed)
- return -EAGAIN;
- }
- wait_event_interruptible(button_waitq, pressed);
- pressed = 0;
- if(copy_to_user(data, &key_val, 1)){
- printk(KERN_ERR "The driver can not copy the data to user area!\n");
- return -ENOMEM;
- }
- return 0;
- }
- static int buttons_close(struct inode *inode, struct file *file){
- free_irq(IRQ_EINT(19), &key_descs[0]);
- free_irq(IRQ_EINT(20), &key_descs[1]);
- free_irq(IRQ_EINT(21), &key_descs[2]);
- free_irq(IRQ_EINT(22), &key_descs[3]);
- free_irq(IRQ_EINT(23), &key_descs[4]);
- return 0;
- }
- static int buttons_fasync (int fd, struct file *filp, int on)
- {
- printk("driver: buttons_fasync\n");
- return fasync_helper (fd, filp, on, &button_async);
- }
- struct file_operations buttons_ops = {
- .open = buttons_open,
- .read = buttons_read,
- .release = buttons_close,
- .fasync = buttons_fasync,
- };
- int buttons_init(void){
- int ret;
- init_timer(&buttons_timer);
- buttons_timer.function = buttons_timer_function;
- //buttons_timer.expires = 0;
- add_timer(&buttons_timer);
- cdev_init(&cdev, &buttons_ops);
- cdev.owner = THIS_MODULE;
- ret = alloc_chrdev_region(&devno, 0, 1, "buttons");
- if(ret){
- printk(KERN_ERR "alloc char device region faild!\n");
- return ret;
- }
- ret = cdev_add(&cdev, devno, 1);
- if(ret){
- printk(KERN_ERR "add char device faild!\n");
- goto add_error;
- }
- buttons_class = class_create(THIS_MODULE, "buttonsdrv");
- if(IS_ERR(buttons_class)){
- printk(KERN_ERR "create class error!\n");
- goto class_error;
- }
- buttons_device = device_create(buttons_class, NULL, devno, NULL, "buttons");
- if(IS_ERR(buttons_device)){
- printk(KERN_ERR "create buttons device error!\n");
- goto device_error;
- }
- init_waitqueue_head(&button_waitq);
- gph3con = (volatile unsigned long *)ioremap(0xE0200C60, 16);
- gph3dat = gph3con + 1;
- *gph3con &= (~0x000000ff);
- *gph3con |=0x11;
- *gph3dat &= ~0x03;
- printk("buttons init\n");
- return 0;
- device_error:
- class_destroy(buttons_class);
- class_error:
- cdev_del(&cdev);
- add_error:
- unregister_chrdev_region(devno,1);
- return -ENODEV;
- }
- void buttons_exit(void){
- printk("buttons exit\n");
- device_destroy(buttons_class, devno);
- class_destroy(buttons_class);
- cdev_del(&cdev);
- unregister_chrdev_region(devno, 1);
- }
- module_init(buttons_init);
- module_exit(buttons_exit);
- MODULE_LICENSE("GPL");
#include <linux/types.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <mach/gpio.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
static dev_t devno;
static struct cdev cdev;
static struct class* buttons_class;
static struct device* buttons_device;
static wait_queue_head_t button_waitq;
static struct timer_list buttons_timer;
static volatile int pressed = 0;
static unsigned int key_val;
static struct fasync_struct *button_async;
struct key_desc{
unsigned int pin;
unsigned char value;
};
static struct key_desc *irq_pd;
static volatile unsigned long *gph3con;
static volatile unsigned long *gph3dat;
static struct key_desc key_descs[8] = {
[0] = {
.pin = S5PV210_GPH2(3),
.value = 0x00,
},
[1] = {
.pin = S5PV210_GPH2(4),
.value = 0x01,
},
[2] = {
.pin = S5PV210_GPH2(5),
.value = 0x02,
},
[3] = {
.pin = S5PV210_GPH2(6),
.value = 0x03,
},
[4] = {
.pin = S5PV210_GPH2(7),
.value = 0x04,
},
};
static void buttons_timer_function(unsigned long data)
{
struct key_desc * pindesc = irq_pd;
unsigned int pinval;
if (!pindesc)
return;
pinval = gpio_get_value(pindesc->pin);
if (pinval)
{
/* 松开 */
key_val = 0x80 | pindesc->value;
}
else
{
/* 按下 */
key_val = pindesc->value;
}
pressed = 1;
wake_up_interruptible(&button_waitq);
kill_fasync (&button_async, SIGIO, POLL_IN);
}
static irqreturn_t buttons_irq(int irq, void *dev_id){
printk("buttons_irq happen\n");
/* 10ms后启动定时器 */
irq_pd = (struct key_desc *)dev_id;
mod_timer(&buttons_timer, jiffies+HZ/100);
return IRQ_RETVAL(IRQ_HANDLED);
}
static int buttons_open(struct inode *inode, struct file *file){
int ret;
ret = request_irq(IRQ_EINT(19), buttons_irq, IRQ_TYPE_EDGE_BOTH, "key1", &key_descs[0]);
if(ret)
return ret;
ret = request_irq(IRQ_EINT(20), buttons_irq, IRQ_TYPE_EDGE_BOTH, "key2", &key_descs[1]);
if(ret)
return ret;
ret = request_irq(IRQ_EINT(21), buttons_irq, IRQ_TYPE_EDGE_BOTH, "key3", &key_descs[2]);
if(ret)
return ret;
ret = request_irq(IRQ_EINT(22), buttons_irq, IRQ_TYPE_EDGE_BOTH, "key4", &key_descs[3]);
if(ret)
return ret;
ret = request_irq(IRQ_EINT(23), buttons_irq, IRQ_TYPE_EDGE_BOTH, "key5", &key_descs[4]);
if(ret)
return ret;
return 0;
}
static ssize_t buttons_read(struct file * file, char __user *data, size_t count, loff_t *loff){
if (count != 1)
return -EINVAL;
if (file->f_flags & O_NONBLOCK)
{
if (!pressed)
return -EAGAIN;
}
wait_event_interruptible(button_waitq, pressed);
pressed = 0;
if(copy_to_user(data, &key_val, 1)){
printk(KERN_ERR "The driver can not copy the data to user area!\n");
return -ENOMEM;
}
return 0;
}
static int buttons_close(struct inode *inode, struct file *file){
free_irq(IRQ_EINT(19), &key_descs[0]);
free_irq(IRQ_EINT(20), &key_descs[1]);
free_irq(IRQ_EINT(21), &key_descs[2]);
free_irq(IRQ_EINT(22), &key_descs[3]);
free_irq(IRQ_EINT(23), &key_descs[4]);
return 0;
}
static int buttons_fasync (int fd, struct file *filp, int on)
{
printk("driver: buttons_fasync\n");
return fasync_helper (fd, filp, on, &button_async);
}
struct file_operations buttons_ops = {
.open = buttons_open,
.read = buttons_read,
.release = buttons_close,
.fasync = buttons_fasync,
};
int buttons_init(void){
int ret;
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;
//buttons_timer.expires = 0;
add_timer(&buttons_timer);
cdev_init(&cdev, &buttons_ops);
cdev.owner = THIS_MODULE;
ret = alloc_chrdev_region(&devno, 0, 1, "buttons");
if(ret){
printk(KERN_ERR "alloc char device region faild!\n");
return ret;
}
ret = cdev_add(&cdev, devno, 1);
if(ret){
printk(KERN_ERR "add char device faild!\n");
goto add_error;
}
buttons_class = class_create(THIS_MODULE, "buttonsdrv");
if(IS_ERR(buttons_class)){
printk(KERN_ERR "create class error!\n");
goto class_error;
}
buttons_device = device_create(buttons_class, NULL, devno, NULL, "buttons");
if(IS_ERR(buttons_device)){
printk(KERN_ERR "create buttons device error!\n");
goto device_error;
}
init_waitqueue_head(&button_waitq);
gph3con = (volatile unsigned long *)ioremap(0xE0200C60, 16);
gph3dat = gph3con + 1;
*gph3con &= (~0x000000ff);
*gph3con |=0x11;
*gph3dat &= ~0x03;
printk("buttons init\n");
return 0;
device_error:
class_destroy(buttons_class);
class_error:
cdev_del(&cdev);
add_error:
unregister_chrdev_region(devno,1);
return -ENODEV;
}
void buttons_exit(void){
printk("buttons exit\n");
device_destroy(buttons_class, devno);
class_destroy(buttons_class);
cdev_del(&cdev);
unregister_chrdev_region(devno, 1);
}
module_init(buttons_init);
module_exit(buttons_exit);
MODULE_LICENSE("GPL");
编译用户测试代码:
- #include <fcntl.h>
- #include <stdio.h>
- #include <poll.h>
- #include <signal.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <fcntl.h>
- int fd;
- void sig_handler(int signum)
- {
- unsigned char key_val;
- read(fd, &key_val, 1);
- printf("key_val: 0x%x\n", key_val);
- }
- int main(int argc, char **argv)
- {
- unsigned char key_val;
- int ret;
- int Oflags;
- signal(SIGIO, sig_handler);
- fd = open("/dev/buttons", O_RDWR | O_NONBLOCK);
- if (fd < 0){
- printf("can't open!\n");
- return -1;
- }
- fcntl(fd, F_SETOWN, getpid());
- Oflags = fcntl(fd, F_GETFL);
- fcntl(fd, F_SETFL, Oflags | FASYNC);
- while(1);
- return 0;
- }
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
int fd;
void sig_handler(int signum)
{
unsigned char key_val;
read(fd, &key_val, 1);
printf("key_val: 0x%x\n", key_val);
}
int main(int argc, char **argv)
{
unsigned char key_val;
int ret;
int Oflags;
signal(SIGIO, sig_handler);
fd = open("/dev/buttons", O_RDWR | O_NONBLOCK);
if (fd < 0){
printf("can't open!\n");
return -1;
}
fcntl(fd, F_SETOWN, getpid());
Oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, Oflags | FASYNC);
while(1);
return 0;
}
测试结果如下:
- [210_Liujia]#insmod buttons.ko
- [210_Liujia]#./buttons_test
- key_val: 0x1
- key_val: 0x81
- key_val: 0x0
- key_val: 0x80
- key_val: 0x2
- key_val: 0x82
- key_val: 0x3
- key_val: 0x83
- key_val: 0x4
- key_val: 0x84
- key_val: 0x1
- key_val: 0x81
- key_val: 0x2
- key_val: 0x82
- key_val: 0x4
- key_val: 0x84
- key_val: 0x3
- key_val: 0x83
[210_Liujia]#insmod buttons.ko
[210_Liujia]#./buttons_test
key_val: 0x1
key_val: 0x81
key_val: 0x0
key_val: 0x80
key_val: 0x2
key_val: 0x82
key_val: 0x3
key_val: 0x83
key_val: 0x4
key_val: 0x84
key_val: 0x1
key_val: 0x81
key_val: 0x2
key_val: 0x82
key_val: 0x4
key_val: 0x84
key_val: 0x3
key_val: 0x83