- 论坛徽章:
- 0
|
#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif
#define __NO__VERSION__
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#ifndef VFIFO_MAJOR
#define VFIFO_MAJOR 241
#endif
#ifndef VFIFO_NR_DEVS
#define VFIFO_NR_DEVS 4
#endif
#ifndef VFIFO_BUFFER
#define VFIFO_BUFFER 4000
#endif
#include <linux/devfs_fs_kernel.h>
devfs_handle_t vfifo_devfs_dir;
int vfifo_major=VFIFO_MAJOR;
int vfifo_nr_devs=VFIFO_NR_DEVS;
int vfifo_buffer=VFIFO_BUFFER;
MODULE_PARM(vfifo_major, "i ");
MODULE_PARM(vfifo_nr_devs, "i ");
MODULE_PARM(vfifo_buffer, "i ");
MODULE_AUTHOR( "CODEPRIMER ");
typedef struct Vfifo_Dev{
wait_queue_head_t rdq,wrq; // rdq表示堵塞读的等待队列,wrq是堵塞写的等待队列
char *base; //所分配缓冲区的起始地址
unsigned int buffersize; //是缓冲区的大小
unsigned int len; //表示管道中已有数据块的长度
unsigned int start; //应该读取得缓冲区位置相对于base的偏移量
unsigned int readers,writers;
//表示VFIFO设备当前的读者个数和写着个数
struct semaphore sem; //用于互斥的访问的信号量
devfs_handle_t r_handle,w_handle; //用于保存设备文件系统的注册句柄,前者表示只读设备,后者表示同一管道的只写设备
}Vfifo_Dev;
struct Vfifo_Dev *vfifo_devices;
char vfifoname[8];
static int vfifo_open(struct inode *inode,struct file *filp)
{
Vfifo_Dev *dev;
int num=MINOR(inode-> i_rdev);
if(!filp-> private_data)
{
if (num> =vfifo_nr_devs*2)
return -ENODEV;
dev=&vfifo_devices[num/2];
filp-> private_data=dev;
}else{
dev=filp-> private_data;
}
if (down_interruptible(&dev-> sem))
return -ERESTARTSYS;
if(!dev-> base){
dev-> base=kmalloc(vfifo_buffer,GFP_KERNEL);
if(!dev-> base){
up(&dev-> sem);
return -ENOMEM;
}
dev-> buffersize=vfifo_buffer;
dev-> len=dev-> start=0;
}
if(filp-> f_mode & FMODE_READ) //****
dev-> readers++;
if(filp-> f_mode & FMODE_WRITE) dev-> writers++; //****
filp-> private_data=dev;
MOD_INC_USE_COUNT;
up(&dev-> sem);
return 0;
}
static int vfifo_release(struct inode *inode,struct file *filp)
{
Vfifo_Dev *dev=filp-> private_data;
down(&dev-> sem);
if(filp-> f_mode&FMODE_READ)
dev-> readers--;
if(filp-> f_mode&FMODE_WRITE){
dev-> writers--;
wake_up_interruptible(&dev-> sem.wait);
}
if((dev-> readers+dev-> writers==0)&&(dev-> len==0)){
kfree(dev-> base);
dev-> base=NULL;
}
up(&dev-> sem);
MOD_DEC_USE_COUNT;
return 0;
}
static ssize_t vfifo_read(struct file *filp,char *buf,size_t count,loff_t *f_pos)
{
Vfifo_Dev *dev=filp-> private_data;
ssize_t read=0;
if(f_pos!=&filp-> f_pos) //****
return -ESPIPE;
if(down_interruptible(&dev-> sem))
return -ERESTARTSYS;
do_more_read:
while(dev-> len==0){
if(!dev-> writers){
up(&dev-> sem);
return -EAGAIN;
}
up(&dev-> sem);
if(filp-> f_flags&O_NONBLOCK)
return -EAGAIN;
printk( "%s reading:going to sleep\n ",current-> comm);
if(wait_event_interruptible(dev-> rdq,(dev-> len> 0)))
return -ERESTARTSYS;
printk( "%s has been waken up\n ",current-> comm);
if(down_interruptible(&dev-> sem))
return -ERESTARTSYS; }
while(count> 0&&dev-> len){
char *pipebuf=dev-> base+dev-> start;
ssize_t chars=dev-> buffersize-dev-> start;
if(chars> count) chars=count;
if(chars> dev-> len) chars=dev-> len;
if(copy_to_user(buf,pipebuf,chars)){
up(&dev-> sem);
return -EFAULT;
}
read+=chars;
dev-> start+=chars;
dev-> start%=dev-> buffersize;
dev-> len-=chars;
count-=chars;
buf+=chars;
}
if (!dev-> len) dev-> start=0;
if(count && dev-> writers && !(filp-> f_flags & O_NONBLOCK)){ //****
up(&dev-> sem);
wake_up_interruptible(&dev-> wrq);
if(down_interruptible(&dev-> sem))
return -ERESTARTSYS;
goto do_more_read;
}
up(&dev-> sem);
wake_up_interruptible(&dev-> wrq);
printk( "%s did read %d bytes\n ",current-> comm, read); //****
return read;
}
static ssize_t vfifo_write(struct file *filp,const char *buf,size_t count,loff_t *f_pos)
{
Vfifo_Dev *dev=filp-> private_data;
ssize_t written=0;
if(f_pos!=&filp-> f_pos||count==0)
return -ESPIPE;
if(down_interruptible(&dev-> sem))
return -ERESTARTSYS;
do_more_write:
while(dev-> len==dev-> buffersize){
up(&dev-> sem);
if(filp-> f_flags&O_NONBLOCK)
return -EAGAIN;
printk( "%s writting:going to sleep\n ",current-> comm);
if(wait_event_interruptible(dev-> wrq,(dev-> len <dev-> buffersize)))
return -ERESTARTSYS;
printk( "%s has been waken up\n ",current-> comm);
if(down_interruptible(&dev-> sem))
return -ERESTARTSYS;
}
while(count> 0){
char *pipebuf=
dev-> base+(dev-> len+dev-> start)%dev-> buffersize;
ssize_t chars=dev-> buffersize-(dev-> len+dev-> start);
if(chars <0) chars+=dev-> start;
if(chars!=0) {
if (chars> count) chars=count;
if (copy_from_user(pipebuf,buf,chars)){
up(&dev-> sem);
return -EFAULT;
}
written+=chars;
dev-> len+=chars;
count-=chars;
buf+=chars;
}
}
if (count&&!(filp-> f_flags&O_NONBLOCK)){
up(&dev-> sem);
wait_up_interruptible(&dev-> rdq); //**** ???
if (down_interruptible(&dev-> sem))
return -ERESTARTSYS;
goto do_more_write;
}
up(&dev-> sem);
wake_up_interruptible(&dev-> rdq); //**** ???
printk( "%s did write %i bytes\n ",current-> comm, written); //****
return written;
}
unsigned int vfifo_poll(struct file *filp,poll_table *wait)
{
Vfifo_Dev *dev=filp-> private_data;
unsigned int mask=0;
poll_wait(filp,&dev-> rdq,wait);
poll_wait(filp,&dev-> wrq,wait);
if (dev-> len> 0) mask|=POLLIN|POLLRDNORM;
if (dev-> len!=dev-> buffersize) mask|=POLLOUT|POLLWRNORM;
return mask;
}
static struct file_operations vfifo_fops={
read: vfifo_read,
write: vfifo_write,
poll: vfifo_poll,
open: vfifo_open,
release:vfifo_release,
};
static int __init vfifo_init_module(void)
{
int result,i;
SET_MODULE_OWNER(&vfifo_fops);
#ifdef CONFIG_DEVFS_FS
vfifo_devfs_dir=devfs_mk_dir(NULL, "vfifo ",NULL);
if(!vfifo_devfs_dir)
return -EBUSY;
#endif
result=devfs_register_chrdev(vfifo_major, "vfifo ",&vfifo_fops);
if (result <0){
printk(KERN_WARNING "vfifo:can 't get major %d\n ",vfifo_major);
return result;
}
if (vfifo_major==0)
vfifo_major=result;
vfifo_devices=kmalloc(vfifo_nr_devs*sizeof(Vfifo_Dev),GFP_KERNEL);
if (!vfifo_devices){
return -ENOMEM;
}
memset(vfifo_devices,0,vfifo_nr_devs*sizeof(Vfifo_Dev));
for (i=0;i <vfifo_nr_devs;i++){
init_waitqueue_head(&vfifo_devices[i].rdq);
init_waitqueue_head(&vfifo_devices[i].wrq);
sema_init(&vfifo_devices[i].sem,1);
#ifdef CONFIG_DEVFS_FS
sprintf(vfifoname, "vfifo%d ",2*i+1);
vfifo_devices[i].w_handle=
devfs_register(vfifo_devfs_dir,vfifoname,DEVFS_FL_NON,vfifo_major,2*i,S_IFCHR|S_IRUGO|S_IWUGO,&vfifo_fops,vfifo_devices+i);
sprintf(vfifoname, "vfifo%d ",2*i+1);
vfifo_devices[i].r_handle=
devfs_register(vfifo_devfs_dir,vfifoname,DEVFS_FL_NON,vfifo_major,2*i+1,S_IFCHR|S_IRUGO|S_IWUGO,&vfifo_fops,vfifo_devices+i);
if (!vfifo_devices[i].r_handle||!vfifo_devices[i].w_handle){
printk(KERN_WARNING "vfifo: can 't register vfifo device nr %i\n ",i);
}
#endif
}
#ifdef VFIFO_DEBUG
create_proc_read_entry( "vfifo ",0,NULL,vfifo_read_mem,NULL);
#endif
return 0;
}
static void __exit vfifo_cleanup_module(void)
{
int i;
devfs_unregister_chrdev(vfifo_major, "vfifo ");
#ifdef VFIFO_DEBUG
remove_proc_entry( "vfifo ",NULL);
#endif
if (vfifo_devices){
for (i=0;i <vfifo_nr_devs;i++){
if (vfifo_devices[i].base)
kfree(vfifo_devices[i].base);
devfs_unregister(vfifo_devices[i].r_handle);
devfs_unregister(vfifo_devices[i].w_handle);
}
kfree(vfifo_devices);
devfs_unregister(vfifo_devfs_dir);
}
}
module_init(vfifo_init_module);
module_exit(vfifo_cleanup_module); |
|