异步通知意思是:一旦设备就绪,则主动通知应用程序,这样应用程序不需要查询设备状态,类似硬件上的“中断”概念。准确的称谓是“信号驱动的异步I/O”.信号是在软件层次上对中断机制的一种模拟。
2. 实现设备操作中的fasync函数,这个函数很简单,其主体就是调用内核的fasync_helper函数。
4. 在驱动的release方法中调用前面定义的fasync函数
阅读(586) | 评论(0) | 转发(1) |
<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
在驱动框架中需要实现xxx_fasync函数,主要涉及一个结构体 struct fasync_struct 和fasync_helper kill_fasync 两个函数。这个结构体和两个函数的声明在include/linux/fs.h中。
- struct fasync_struct {
- spinlock_t fa_lock;
- int magic;
- int fa_fd;
- struct fasync_struct *fa_next; /* singly linked list */
- struct file *fa_file;
- struct rcu_head fa_rcu;
- };
- /* SMP safe fasync helpers: */
- extern int fasync_helper(int, struct file *, int, struct fasync_struct **);
- /* can be called from interrupts */
- extern void kill_fasync(struct fasync_struct **, int, int);
函数的实现是在:fs/fcntl.c: fasync_helper函数经常在字符设备中用来申请和删除异步队列。如果删除则 on= -1;在release函数中使用删除模式。
- /*
- * fasync_helper() is used by almost all character device drivers
- * to set up the fasync queue, and for regular files by the file
- * lease code. It returns negative on error, 0 if it did no changes
- * and positive if it added/deleted the entry.
- */
- int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
- {
- if (!on)
- return fasync_remove_entry(filp, fapp);
- return fasync_add_entry(fd, filp, fapp);
- }
-
- EXPORT_SYMBOL(fasync_helper);
xxx_fasync的驱动基本构架是:
1. 在设备抽象的数据结构中增加一个struct fasync_struct的指针
- struct cfifo_t{
- struct cdev cdev;
- unsigned char mem[GLOBALMEM_SIZE];
- struct semaphore sem;
-
- unsigned int current_len;
- wait_queue_head_t r_wait;
- wait_queue_head_t w_wait;
-
- struct fasync_struct *r_async_queue; /* 异步指针结构体指针 读*/
- };
设备的fasync函数:
- static int cfifo_fasync(int fd, struct file *filp, int mode)
- {
- struct cfifo_t *dev = filp->private_data;
- return fasync_helper(fd, filp, mode, &dev->r_async_queue);
- }
3. 在需要向用户空间通知的地方(例如wrie方法中)调用内核的kill_fasync函数。
- static ssize_t cfifo_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
- {
- int ret = 0;
- struct cfifo_t *dev = filp->private_data;
- DECLARE_WAITQUEUE(wait, current);
-
- down(&dev->sem);
- add_wait_queue(&dev->w_wait, &wait);
-
- while(dev->current_len >= GLOBALMEM_SIZE){
- if(filp->f_flags & O_NONBLOCK){
- ret = -EAGAIN;
- goto out;
- }
-
- __set_current_state(TASK_INTERRUPTIBLE); /* 进程状态,定义在 linux/sched.h */
- up(&dev->sem); /* 释放信号量,避免死锁 */
-
- schedule(); /* 调度其他进程运行*/
- if(signal_pending(current)){
- ret = -ERESTARTSYS;
- goto out2;
- }
-
- down(&dev->sem);
- }
-
- if(count > GLOBALMEM_SIZE - dev->current_len)
- count = GLOBALMEM_SIZE - dev->current_len;
-
-
- if(copy_from_user(dev->mem+dev->current_len, buf, count)){
- ret = - EFAULT;
- goto out;
- }
- else{
- dev->current_len += count;
- *fpos = dev->current_len;
- ret = count;
-
- wake_up_interruptible(&dev->r_wait);
- printk(KERN_INFO "write %s %d bites \n", buf, count);
-
- /* 产生异步信号 */
- if(NULL != dev->r_async_queue)
- kill_fasync(&dev->r_async_queue, SIGIO, POLL_IN);
- }
-
- out:
- up(&dev->sem);
- out2:
- remove_wait_queue(&dev->w_wait, &wait);
- set_current_state(TASK_RUNNING);
-
- return ret;
- }
- static int cfifo_release(struct inode *inode, struct file *filp)
- {
- cfifo_fasync(-1, filp, 0); /* 文件从异步通知队列中删除 */
- filp->private_data = NULL;
- return 0;
- }
- static int cfifo_release(struct inode *inode, struct file *filp)
测试程序:
- /**
- * =====================================================================================
- * Filename: cfifo_signal_test.c
- * Description: 测试cfifo 驱动信号
- *
- * Created: 2011年07月02日 17时13分32秒
- * Revision: none
- * Compiler: gcc
- *
- * Author: wanghuan
- * Company:
- * =====================================================================================
- */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <signal.h>
-
- void input_handler(int signum)
- {
- printf("[%s %s %d] Sinnal Num = %d\n", __FUNCTION__, __FILE__, __LINE__, signum);
- }
-
- int main()
- {
- int fd, oflags;
- fd = open("/dev/cfifo0", O_RDWR, S_IRUSR| S_IRUSR);
- if(fd != -1)
- {
- signal(SIGIO, input_handler);
- fcntl(fd, F_SETOWN, getpid());
- oflags = fcntl(fd, F_GETFL);
- fcntl(fd, F_SETFL, oflags | FASYNC);
- while(1)
- {
- sleep(100);
- }
- }
- else
- perror("open");
-
- return 0;
- }
分析一下测试程序看signal工作的流程:
1.设置信号的处理函数。 signal(SIGIO, input_handler);
2.将当文件和进程号关联,确定信号发射对象。fcntl(fd, F_SETOWN, getpid());
3.设置当前的文件支持异步通知,这一步实际上就是调用驱动中的xxx_fasync 函数,使fasync_helper分配异步通知队列。
4.当向文件中写入数据时在wrtie方法中调到
- if(NULL != dev->r_async_queue)
- kill_fasync(&dev->r_async_queue, SIGIO, POLL_IN);
- 这样就会产生SIGIO信号。
完整的程序:
- /**
- * =====================================================================================
- * Filename: cfifo.c
- * Description: 字符设备驱动模型 阻塞I/O
- *
- * Created: 2011年06月12日 17时19分50秒
- * Revision: none
- * Compiler: gcc
- *
- * Author: wanghuan,
- * Company:
- * =====================================================================================
- */
-
- #include <linux/module.h>
- #include <linux/types.h>
- #include <linux/fs.h> /* file_operation */
- #include <linux/errno.h> /* Error number */
- #include <linux/mm.h>
- #include <linux/sched.h>
- #include <linux/slab.h>
- #include <linux/init.h> /* __init __exit */
- #include <linux/device.h>
- #include <linux/cdev.h>
- #include <linux/poll.h>
- #include <asm/io.h>
- #include <asm/system.h>
- #include <asm/uaccess.h> /* copy_to_user, copy_from_user */
- #include <linux/kernel.h> /* printk() */
-
- #define GLOBALMEM_SIZE 0x1000
- #define MEM_CLEAR 0x1
- #define DEV_NAME "cfifo"
- //#define MEM_CLEAR _IO(GLOBALMEM_SIZE, 0)
-
-
- static int MAJOR_NR = 255; /* Driver Major Number */
- //static int MINOR_NR = 0; /* Driver Major Number */
-
- struct cfifo_t{
- struct cdev cdev;
- unsigned char mem[GLOBALMEM_SIZE];
- struct semaphore sem;
-
- unsigned int current_len;
- wait_queue_head_t r_wait;
- wait_queue_head_t w_wait;
-
- struct fasync_struct *r_async_queue; /* 异步指针结构体指针 读*/
- struct fasync_struct *w_async_queue; /* 异步指针结构体指针 写*/
- };
- static struct cfifo_t *cfifo_p;
-
- static ssize_t cfifo_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
- {
- int ret = 0;
- struct cfifo_t *dev = filp->private_data;
- DECLARE_WAITQUEUE(wait, current);
-
- down(&dev->sem);
- add_wait_queue(&dev->r_wait, &wait);
-
- while(dev->current_len == 0){
- if(filp->f_flags & O_NONBLOCK){
- ret = -EAGAIN;
- goto out;
- }
-
- __set_current_state(TASK_INTERRUPTIBLE);
- up(&dev->sem);
-
- schedule(); /*调度其他进程运行*/
- if(signal_pending(current)){
- ret = -ERESTARTSYS;
- goto out2;
- }
-
- down(&dev->sem);
- }
-
- if(count > dev->current_len)
- count = dev->current_len;
-
- if(copy_to_user(buf, (void*)(dev->mem), count)){
- ret = -EFAULT;
- goto out;
- } else{
- memcpy(dev->mem, dev->mem + count, dev->current_len - count);
- dev->current_len -= count;
- *fpos =dev->current_len;
-
- wake_up_interruptible(&dev->w_wait);
- ret =count;
-
- printk(KERN_INFO "read %s %d bites \n", buf, count);
- }
-
- out:
- up(&dev->sem);
- out2:
- remove_wait_queue(&dev->r_wait, &wait);
- set_current_state(TASK_RUNNING);
-
- return ret;
- }
-
- static ssize_t cfifo_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
- {
- int ret = 0;
- struct cfifo_t *dev = filp->private_data;
- DECLARE_WAITQUEUE(wait, current);
-
- down(&dev->sem);
- add_wait_queue(&dev->w_wait, &wait);
-
- while(dev->current_len >= GLOBALMEM_SIZE){
- if(filp->f_flags & O_NONBLOCK){
- ret = -EAGAIN;
- goto out;
- }
-
- __set_current_state(TASK_INTERRUPTIBLE); /* 进程状态,定义在 linux/sched.h */
- up(&dev->sem); /* 释放信号量,避免死锁 */
-
- schedule(); /* 调度其他进程运行*/
- if(signal_pending(current)){
- ret = -ERESTARTSYS;
- goto out2;
- }
-
- down(&dev->sem);
- }
-
- if(count > GLOBALMEM_SIZE - dev->current_len)
- count = GLOBALMEM_SIZE - dev->current_len;
-
-
- if(copy_from_user(dev->mem+dev->current_len, buf, count)){
- ret = - EFAULT;
- goto out;
- }
- else{
- dev->current_len += count;
- *fpos = dev->current_len;
- ret = count;
-
- wake_up_interruptible(&dev->r_wait);
- printk(KERN_INFO "write %s %d bites \n", buf, count);
-
- /* 产生异步信号 */
- if(NULL != dev->r_async_queue)
- kill_fasync(&dev->r_async_queue, SIGIO, POLL_IN);
- }
-
- out:
- up(&dev->sem);
- out2:
- remove_wait_queue(&dev->w_wait, &wait);
- set_current_state(TASK_RUNNING);
-
- return ret;
- }
-
- static loff_t cfifo_llseek(struct file *filp, loff_t offset, int orig)
- {
- loff_t ret;
- switch(orig){
- case 0:
- if(offset < 0){
- ret = -EFAULT;
- break;
- }
- if((unsigned int)offset > GLOBALMEM_SIZE){
- ret = -EFAULT;
- break;
- }
-
- filp->f_pos = (unsigned int)offset;
- ret = filp->f_pos;
- break;
- case 1:
- if(filp->f_pos + offset > GLOBALMEM_SIZE){
- ret = -EFAULT;
- break;
- }
-
- if(filp->f_pos + offset < 0){
- ret = -EFAULT;
- break;
- }
-
- filp->f_pos += offset;
- ret = filp->f_pos;
- break;
- default:
- ret = -EFAULT;
- }
- return ret;
- }
-
- static int cfifo_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
- {
- struct cfifo_t *dev = filp->private_data;
- switch(cmd){
- case MEM_CLEAR:
- if(down_interruptible(&dev->sem))
- return -ERESTARTSYS;
-
- memset(dev->mem, 0, GLOBALMEM_SIZE);
- dev->current_len = 0;
- filp->f_pos = 0;
-
- up(&dev->sem);
- printk(KERN_INFO "globalmem is set to zero \n");
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
- }
-
- static unsigned int cfifo_poll(struct file *file, poll_table *wait)
- {
- unsigned int mask = 0;
- struct cfifo_t *dev = file->private_data;
-
- down(&dev->sem);
- poll_wait(file, &dev->r_wait, wait);
- poll_wait(file, &dev->w_wait, wait);
-
- if(dev->current_len != 0)
- mask |= POLLIN | POLLRDNORM;
-
- if(dev->current_len != GLOBALMEM_SIZE)
- mask |= POLLOUT | POLLWRNORM;
-
- up(&dev->sem);
- return mask;
- }
-
- static int cfifo_fasync(int fd, struct file *filp, int mode)
- {
- struct cfifo_t *dev = filp->private_data;
- return fasync_helper(fd, filp, mode, &dev->r_async_queue);
- }
-
- static int cfifo_open(struct inode *inode, struct file *filp)
- {
- struct cfifo_t *dev;
- dev = container_of(inode->i_cdev, struct cfifo_t, cdev);
- filp->private_data = dev;
- return 0;
- }
-
- static int cfifo_release(struct inode *inode, struct file *filp)
- {
- cfifo_fasync(-1, filp, 0); /* 文件从异步通知队列中删除 */
- filp->private_data = NULL;
- return 0;
- }
-
- static const struct file_operations cfifo_fops = {
- .owner = THIS_MODULE,
- .open = cfifo_open,
- .release = cfifo_release,
- .read = cfifo_read,
- .write = cfifo_write,
- .ioctl = cfifo_ioctl,
- .llseek = cfifo_llseek,
- .poll = cfifo_poll,
- .fasync = cfifo_fasync,
- };
-
- static void cfifo_setup_cdev(struct cfifo_t *cfifo_cdev, int minor)
- {
- int err;
- dev_t devno = MKDEV(MAJOR_NR, minor);
-
- cdev_init(&cfifo_cdev->cdev, &cfifo_fops);
- cfifo_p->cdev.owner = THIS_MODULE;
- err = cdev_add(&cfifo_cdev->cdev, devno, 1);
- if(err != 0)
- printk(KERN_NOTICE "Error %d adding gmen", err);
-
- cfifo_cdev->current_len = 0;
- init_MUTEX(&cfifo_cdev->sem);
- init_waitqueue_head(&cfifo_cdev->r_wait);
- init_waitqueue_head(&cfifo_cdev->w_wait);
- }
-
- static struct class *cdev_class;
- static int __init cfifo_cdev_init(void)
- {
- int result;
- dev_t devno = MKDEV(MAJOR_NR, 0);
-
- if(0 != MAJOR_NR){
- result = register_chrdev_region(devno, 1, DEV_NAME); //注册设备 支持两个设备,此设备号从0开始
- } else {
- result = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);
- MAJOR_NR = MAJOR(devno);
- }
-
- printk(KERN_CRIT"hello cfifo\n");
- if(result < 0)
- return result;
-
- cfifo_p = kmalloc(2*sizeof(struct cfifo_t), GFP_KERNEL);
- if(NULL == cfifo_p){
- unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 1);
- result = -ENOMEM;
- }
-
- memset(cfifo_p, 0, 1*sizeof(struct cfifo_t));
- cfifo_setup_cdev(&cfifo_p[0], 0);
- cfifo_setup_cdev(&cfifo_p[0], 1);
-
- cdev_class = class_create(THIS_MODULE, DEV_NAME);
- if(IS_ERR(cdev_class))
- return PTR_ERR(cdev_class);
-
- device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 0), NULL, DEV_NAME"%d", 0);
- device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 1), NULL, DEV_NAME"%d", 1);
- return 0;
- }
-
- void cfifo_cdev_exit(void)
- {
- cdev_del(&cfifo_p[0].cdev);
- cdev_del(&cfifo_p[0].cdev);
-
- device_destroy(cdev_class, MKDEV(MAJOR_NR, 0)); //delete device node under /dev
- device_destroy(cdev_class, MKDEV(MAJOR_NR, 1)); //delete device node under /dev
- class_destroy(cdev_class); //delete class created by us
-
- kfree(cfifo_p);
- unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 1); //由于注册了两个设备,最后一个参数为 2
- }
-
- module_init(cfifo_cdev_init);
- module_exit(cfifo_cdev_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("wanghuan");
相关热门文章
给主人留下些什么吧!~~
评论热议