异步通知意思是:一旦设备就绪,则主动通知应用程序,这样应用程序不需要查询设备状态,类似硬件上的“中断”概念。准确的称谓是“信号驱动的异步I/O”.信号是在软件层次上对中断机制的一种模拟。 在驱动框架中需要实现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; /* 异步指针结构体指针 读*/
-
};
2. 实现设备操作中的fasync函数,这个函数很简单,其主体就是调用内核的fasync_helper函数。
设备的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;
-
}
4. 在驱动的release方法中调用前面定义的fasync函数- static int cfifo_release(struct inode *inode, struct file *filp)
-
{
-
cfifo_fasync(-1, filp, 0); /* 文件从异步通知队列中删除 */
-
filp->private_data = NULL;
-
return 0;
-
}
测试程序:
- /**
-
* =====================================================================================
-
* 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");
阅读(1502) | 评论(0) | 转发(1) |