- 论坛徽章:
- 0
|
2_3_2___pipe.c
#include linux/init.h>
#include linux/module.h>
#include linux/kernel.h>
#include linux/slab.h>
#include linux/cdev.h>
#include linux/string.h>
#include linux/fs.h>
#include linux/bitops.h>
#include linux/spinlock.h>
#include linux/wait.h>
#include linux/sched.h>
#include linux/errno.h>
#include linux/poll.h>
#include asm/uaccess.h>
#include "pipe.h"
#define AUTHOR "Zoulinzhi"
#define DESCRIPTION "two endpoint char-stream pipe driver"
static void data_init( struct data_buff *buff )
{
DPRINTK("+data_init()\n");
spin_lock_init( &buff->lock );
buff->inF = 0;
init_waitqueue_head( &buff->inQ );
buff->outF = 0;
init_waitqueue_head( &buff->outQ );
buff->in = 0;
buff->out = 0;
DPRINTK("-data_init()\n");
}
static ssize_t data_write( struct file *file, struct data_buff *buff, const char __user *from, size_t size )
{
unsigned long flags;
size_t l;
char *mem;
ssize_t ret;
DPRINTK("+data_write()\n");
do{
/*
* during the spin lock and unlock
* we won't call copy_from_user
* to prevent re-entry
* so we copy data from user space first
* then call memcpy during spin lock and unlock
* this method will decrease the performance
* because we may copy unneed data and cpoy twice.
*/
if( size > DATA_BUFF_SIZE ){
size = DATA_BUFF_SIZE;
}
mem = kmalloc( size, GFP_KERNEL );
if( mem == NULL ){
DPRINTK( "kmalloc error\n" );
ret = -ENOMEM;
break;
}
if( copy_from_user( mem, from, size ) ){
DPRINTK( "copy_from_user error\n");
ret = -EFAULT;
break;
}
/* check buff space usable */
spin_lock_irqsave( &buff->lock, flags );
ret = 0;
while( ( buff->in - buff->out ) >= DATA_BUFF_SIZE ){
spin_unlock_irqrestore( &buff->lock, flags );
/* NONBLOCK write don't need to be sleeped */
if( file->f_flags & O_NONBLOCK ){
ret = -EAGAIN;
break;
}
DPRINTK( "no space, sleep ....\n" );
if( wait_event_interruptible( buff->inQ, buff->inF != 0 ) ){
DPRINTK( "signal interrupt\n" );
ret = -ERESTARTSYS;
break;
}
else{
buff->inF = 0;
}
spin_lock_irqsave( &buff->lock, flags );
}
if( ret ){ break; }
/* copy data to data_buff */
size = min( size, DATA_BUFF_SIZE - ( buff->in - buff->out ) );
l = min( size, DATA_BUFF_SIZE - ( buff->in % DATA_BUFF_SIZE ) );
memcpy( buff->data + ( buff->in % DATA_BUFF_SIZE ), mem, l );
memcpy( buff->data, mem + l, size - l );
buff->in += size;
ret = size;
spin_unlock_irqrestore( &buff->lock, flags );
DPRINTK( "%d bytes write\n", size );
/* wake up read queue */
buff->outF = 1;
wake_up_interruptible( &buff->outQ );
}
while( 0 );
/* free the alloc memory */
if( mem ){ kfree( mem ); }
DPRINTK("-data_write()\n");
return ret;
}
static ssize_t data_read( struct file *file, struct data_buff *buff, char __user *to, size_t size )
{
unsigned long flags;
size_t l;
char *mem;
ssize_t ret;
DPRINTK("+data_read()\n");
do{
mem = kmalloc( DATA_BUFF_SIZE, GFP_KERNEL );
if( mem == NULL ){
DPRINTK( "kmalloc error\n" );
ret = -ENOMEM;
break;
}
/* check data usable */
spin_lock_irqsave( &buff->lock, flags );
ret = 0;
while( buff->in - buff->out == 0 ){
spin_unlock_irqrestore( &buff->lock, flags );
/* NONBLOCK read don't need to be sleeped */
if( file->f_flags & O_NONBLOCK ){
ret = -EAGAIN;
break;
}
DPRINTK( "no data, sleep ....\n" );
if( wait_event_interruptible( buff->outQ, buff->outF != 0 ) ){
DPRINTK( "signal interrupt\n" );
ret = -ERESTARTSYS;
break;
}
else{
buff->outF = 0;
}
spin_lock_irqsave( &buff->lock, flags );
}
if( ret ){ break; }
/* copy data to temp buff */
size = min( size, buff->in - buff->out );
l = min( size, DATA_BUFF_SIZE - ( buff->out % DATA_BUFF_SIZE ) );
memcpy( mem, buff->data + ( buff->out % DATA_BUFF_SIZE ), l );
memcpy( mem + l, buff->data, size - l );
buff->out += size;
ret = size;
if( buff->in == buff->out){
buff->in = buff->out = 0;
}
spin_unlock_irqrestore( &buff->lock, flags );
/* wake up write queue */
buff->inF = 1;
wake_up_interruptible( &buff->inQ );
/* copy data to user space */
if( copy_to_user( to, mem, size ) ){
DPRINTK( "copy to user error\n");
ret = -ENOMEM;
}
else{
DPRINTK( "%d bytes readed\n", size );
}
}
while( 0 );
/* free the alloc memory */
if( mem ){ kfree( mem ); }
DPRINTK("-data_read()\n");
return ret;
}
static unsigned int pipe_major;
static unsigned int pipe_minor;
static dev_t pipe_dev;
static struct pipe_chrdev pipe_chrdev[ PIPE_NR_DEVS ];
static ssize_t pipe_read( struct file *file, char __user *buf, size_t size, loff_t *ppos )
{
struct pipe_chrdev *dev;
ssize_t ret;
DPRINTK("+pipe_read()\n");
dev = file->private_data;
ret = data_read( file, dev->rb, buf, size );
DPRINTK("-pipe_read()\n");
return ret;
}
static ssize_t pipe_write( struct file *file, const char __user *buf, size_t size, loff_t *ppos )
{
struct pipe_chrdev *dev;
ssize_t ret;
DPRINTK("+pipe_write()\n");
dev = file->private_data;
ret = data_write( file, dev->wb, buf, size );
DPRINTK("-pipe_write()\n");
return ret;
}
static int pipe_open( struct inode *inode, struct file *file )
{
int i,ret;
DPRINTK("+pipe_open()\n");
ret = -EINVAL;
i = iminor( inode );
if( i PIPE_NR_DEVS ){
file->private_data = &pipe_chrdev[ i ];
spin_lock( &pipe_chrdev[ i ].olock );
if( pipe_chrdev[ i ].ocnt ){
DPRINTK( "pipe%d opened : failed\n", i );
ret = -EBUSY;
}
else{
pipe_chrdev[ i ].ocnt ++;
ret = 0;
}
spin_unlock( &pipe_chrdev[ i ].olock );
}
DPRINTK("-pipe_open()\n");
return ret;
}
static int pipe_release( struct inode *inode, struct file *file )
{
int i;
DPRINTK("+pipe_release()\n");
i = iminor( inode );
if( i PIPE_NR_DEVS ){
spin_lock( &pipe_chrdev[ i ].olock );
pipe_chrdev[ i ].ocnt = 0;
spin_unlock( &pipe_chrdev[ i ].olock );
}
DPRINTK("-pipe_release()\n");
return 0;
}
static unsigned int pipe_poll( struct file *file, struct poll_table_struct *ptable )
{
struct pipe_chrdev *dev;
unsigned int mask;
unsigned long flags;
DPRINTK("+pipe_poll()\n");
mask = 0;
dev = file->private_data;
poll_wait( file, &dev->buff.inQ, ptable );
poll_wait( file, &dev->buff.outQ, ptable );
spin_lock_irqsave( &dev->buff.lock, flags );
/* readable */
if( dev->buff.in != dev->buff.out ){
DPRINTK( " select : readable \n");
mask |= POLLIN | POLLRDNORM;
}
/* writable */
if( dev->buff.in - dev->buff.out DATA_BUFF_SIZE ){
DPRINTK( " select : writable \n");
mask |= POLLOUT | POLLWRNORM;
}
spin_unlock_irqrestore( &dev->buff.lock, flags );
DPRINTK("-pipe_poll()\n");
return mask;
}
struct file_operations pipe_fops = {
.owner = THIS_MODULE,
.read = pipe_read,
.write = pipe_write,
.open = pipe_open,
.release = pipe_release,
.poll = pipe_poll,
};
static int pipe_setup_cdev( struct pipe_chrdev *chrdev, int index )
{
int result;
int devno;
struct cdev *cdev;
DPRINTK("+pipe_setup_cdev()\n");
cdev = cdev_alloc();
if( cdev ){
cdev_init( cdev, &pipe_fops );
chrdev->rb = NULL;
chrdev->wb = NULL;
chrdev->ocnt = 0;
spin_lock_init( &chrdev->olock );
chrdev->index = index;
chrdev->cdev = cdev;
chrdev->cdev->owner = THIS_MODULE;
chrdev->cdev->ops = &pipe_fops;
devno = MKDEV( pipe_major, pipe_minor + index );
result = cdev_add( chrdev->cdev, devno, 1 );
}
else{
result = -ENOMEM;
}
DPRINTK("-pipe_setup_cdev()\n");
return result;
}
static void pipe_free( void )
{
struct cdev *cdev;
DPRINTK("+pipe_free()\n");
cdev = pipe_chrdev[0].cdev;
if( cdev ){
cdev_del( cdev );
}
cdev = pipe_chrdev[1].cdev;
if( cdev ){
cdev_del( cdev );
}
unregister_chrdev_region( pipe_dev, PIPE_NR_DEVS );
DPRINTK("-pipe_free()\n");
}
static int __init pipe_init( void )
{
int result;
DPRINTK("+pipe_init()\n");
do{
pipe_major = 0;
pipe_minor = 0;
/* alloc char device number first */
if( pipe_major ){
pipe_dev= MKDEV( pipe_major, pipe_minor );
result = register_chrdev_region( pipe_dev, PIPE_NR_DEVS, "pipe" );
}
else{
result = alloc_chrdev_region( &pipe_dev, pipe_minor, PIPE_NR_DEVS, "pipe" );
pipe_major = MAJOR( pipe_dev );
}
if( result 0 ){
DPRINTK( "cann't get major %d\n", pipe_major );
break;
}
/* register char device into kernel second */
result = pipe_setup_cdev( &pipe_chrdev[0], 0 );
if( result ){
DPRINTK( "pipe_setup_cdev 0: failed\n" );
break;
}
result = pipe_setup_cdev( &pipe_chrdev[1], 1 );
if( result ){
DPRINTK( "pipe_setup_cdev 1: failed\n" );
break;
}
/* initial vars */
data_init( &pipe_chrdev[0].buff );
data_init( &pipe_chrdev[1].buff );
pipe_chrdev[0].rb = &pipe_chrdev[0].buff;
pipe_chrdev[0].wb = &pipe_chrdev[1].buff;
pipe_chrdev[1].rb = &pipe_chrdev[1].buff;
pipe_chrdev[1].wb = &pipe_chrdev[0].buff;
printk( DESCRIPTION "\n");
DPRINTK("-pipe_init()\n");
return 0;
}
while( 0 );
pipe_free();
DPRINTK("-pipe_init()\n");
return result;
}
static void __exit pipe_exit( void )
{
DPRINTK("+pipe_exit()\n");
pipe_free();
DPRINTK("-pipe_exit()\n");
}
module_init( pipe_init );
module_exit( pipe_exit );
MODULE_AUTHOR( AUTHOR );
MODULE_DESCRIPTION( DESCRIPTION );
MODULE_LICENSE( "BSD" );
返回目录
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/68424/showart_681422.html |
|