LINUX设备驱动程序阅读同步代码之二(scull管道)

#include<linux/module.h>
#include<linux/init.h>
#include<linux/list.h>
#include<linux/fs.h>
#include<linux/types.h>
#include<linux/kdev_t.h>
#include<linux/cdev.h>
#include<linux/errno.h>
#include<linux/slab.h>
#include<asm/uaccess.h>
#include<asm/ioctl.h>
#include<linux/ioctl.h>
#include<linux/semaphore.h>
#include<linux/sched.h>
#include<linux/poll.h>

#define NR_DEVS 1
#define BUFFERSIZE 2048


MODULE_LICENSE("Dual BSD/GPL");

//************************************************
//下边这些是该设备模块的全局数据区

int scull_major=0;
int scull_minor=0;
unsigned int scull_nr_devs=NR_DEVS;
int buffersize=BUFFERSIZE;




struct scull_pipe
{
    wait_queue_head_t inq,outq;
    char *buffer,*end;
    int buffersize;
    char *rp,*wp;
    int nreaders,nwriters;
    struct fasync_struct *async_queue;
    struct semaphore sem; //在此加入了信号量,互斥锁
    struct cdev cdev;
} scull_pipe;


//**************************************************************
// 赋值给struct file_operations结构的函数指针成员的函数
//*************************************************************


ssize_t spacefree(struct scull_pipe *pipe)
{
    if( pipe->rp == pipe->wp )
        return pipe->buffersize-1;
    return ((pipe->rp-pipe->wp+pipe->buffersize)%pipe->buffersize)-1;
}

static int scull_getwritespace(struct scull_pipe *dev,struct file * filp)
{
    while(spacefree(dev)==0)
    {
        DEFINE_WAIT(wait);
        up(&dev->sem);
        if(filp->f_flags&O_NONBLOCK)
            return -EAGAIN;
        printk(KERN_INFO "\"%s\" writing: going to sleep\n",current->comm);
        prepare_to_wait(&dev->outq,&wait,TASK_INTERRUPTIBLE);
        if(spacefree(dev)==0)
            schedule();
        finish_wait(&dev->outq,&wait);
        if(signal_pending(current))
            return -ERESTARTSYS;
        if(down_interruptible(&dev->sem))
            return -ERESTARTSYS;
    }
    printk(KERN_INFO "here is in scull_getwritespace before return 0");
    return 0;
}

ssize_t scull_p_read(struct file *filp,char __user *buf,size_t count,loff_t *f_pos)
{
    struct scull_pipe *dev = filp->private_data;
    if( down_interruptible(&dev->sem) )
        return -ERESTARTSYS;
    while(dev->rp == dev->wp )
    {
        up(&dev->sem);
        if(filp->f_flags & O_NONBLOCK)
            return -EAGAIN;
        printk(KERN_INFO "\"%s\" reading: going to sleep\n",current->comm);
        if(wait_event_interruptible(dev->inq,(dev->rp != dev->wp)))
            return -ERESTARTSYS;
        if(down_interruptible(&dev->sem))
            return -ERESTARTSYS;
    }
    if( dev->wp > dev->rp )
        count = min(count,(size_t)(dev->wp-dev->rp));
    else
        count = min(count,(size_t)(dev->end-dev->rp));
    if(copy_to_user(buf,dev->rp,count))
    {
        up(&dev->sem);
        return -EFAULT;
    }
    dev->rp += count;
    if(dev->rp == dev->end)
        dev->rp = dev->buffer;
    up(&dev->sem);
    wake_up_interruptible(&dev->outq);
    printk("\"%s\" did read %li bytes\n",current->comm,(long)count);
    return count;
}

ssize_t scull_p_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_pos)
{
    struct scull_pipe *dev = filp->private_data;
    int result;
    printk(KERN_INFO "start scull_p_write\n");
    printk(KERN_INFO "1end of scull_p_write\n");
    if( down_interruptible(&dev->sem) )
        return -ERESTARTSYS;
    result = scull_getwritespace(dev,filp);
    printk(KERN_INFO "2end of scull_p_write\n");
    
    if( result )
        return result;
    printk(KERN_INFO "3end of scull_p_write\n");
    count = min(count,(size_t)spacefree(dev));
    printk(KERN_INFO "4end of scull_p_write\n");
    if(dev->wp >= dev->rp )
        count = min(count,(size_t)(dev->end-dev->wp));
    else
        count = min(count,(size_t)(dev->rp-dev->wp-1));

    printk(KERN_INFO "5end of scull_p_write\n");
    if( copy_from_user(dev->wp,buf,count))
    {
        up(&dev->sem);
        return -EFAULT;
    }
    printk(KERN_INFO "6end of scull_p_write\n");
    dev->wp += count;
    if(dev->wp==dev->end)
        dev->wp=dev->buffer;
    printk(KERN_INFO "7end of scull_p_write\n");
    up(&dev->sem);
    wake_up_interruptible(&dev->inq);
    if(dev->async_queue)
        kill_fasync(&dev->async_queue,SIGIO,POLL_IN);
    printk(KERN_INFO "8end of scull_p_write\n");
    return count;       
}


int scull_p_open(struct inode * inode,struct file *filp)
{
    struct scull_pipe *dev;
    dev = container_of(inode->i_cdev,struct scull_pipe,cdev);
    filp->private_data = dev;
    printk(KERN_INFO "here is end of scull_open\n");
    if( filp->f_mode &FMODE_READ )
        dev->nreaders++;
    if( filp->f_mode &FMODE_WRITE )
        dev->nwriters++;

    return 0;
}

static unsigned int scull_p_poll(struct file *filp,poll_table *wait)
{
    struct scull_pipe *dev = filp->private_data;
    unsigned int mask=0;

    down(&dev->sem);
    printk(KERN_INFO "the scull_p_poll is called by vfs now!!!\n");
    poll_wait(filp,&dev->inq,wait);
    poll_wait(filp,&dev->outq,wait);
    if(dev->rp!=dev->wp)
        mask |= POLLIN|POLLRDNORM;
    if(spacefree(dev))
        mask |= POLLOUT |POLLWRNORM;
    up(&dev->sem);
    return mask;
}

struct file_operations scull_fops=
{
    .owner = THIS_MODULE,
    .read = scull_p_read,
    .write = scull_p_write,    
    .open = scull_p_open,
    .poll = scull_p_poll,
};
//*************************************************************
//以下是模块初始化和模块退出代码
//************************************************************


int get_major(void)
{
    dev_t dev;
    int result;
    if( scull_major )
    {
        dev = MKDEV(scull_major,scull_minor);
        result=register_chrdev_region(dev,scull_nr_devs,"scull");
    }
    else
    {
        result=alloc_chrdev_region(&dev,scull_minor,scull_nr_devs,"scull");
        scull_major = MAJOR(dev);
    }
    if( result<0 )
    {
        printk(KERN_INFO "scull: can't get major %d\n",scull_major);
        return result;
    }
    return result;
}


static void scull_setup_cdev(struct scull_pipe *dev,int index)
{
    int err,devno = MKDEV(scull_major,scull_minor+index);
    cdev_init(&dev->cdev,&scull_fops);
    dev->cdev.owner = THIS_MODULE;
    err = cdev_add(&dev->cdev,devno,scull_nr_devs);
    if( err )
        printk(KERN_INFO "error %d adding scull %d",err,index);
}

static int scull_init(void)
{
    int i=0;
    printk(KERN_INFO "scull_init \n");
    get_major();
    scull_setup_cdev(&scull_pipe,i);
    scull_pipe.buffersize = buffersize;
    scull_pipe.buffer = kmalloc(scull_pipe.buffersize,GFP_KERNEL);
    if( !scull_pipe.buffer )
    {
        printk(KERN_INFO "kmalloc for buffer failed now,please rmmod the module\n");
        return -1;
    }
    scull_pipe.rp = scull_pipe.buffer;
    scull_pipe.wp = scull_pipe.buffer;
    scull_pipe.end = scull_pipe.buffer+scull_pipe.buffersize;
    scull_pipe.nreaders = 0;
    scull_pipe.nwriters = 0;
    sema_init(&scull_pipe.sem,1); 
    init_waitqueue_head(&scull_pipe.inq);
    init_waitqueue_head(&scull_pipe.outq);
    
    return 0;
}

static void scull_exit(void)
{
    printk(KERN_INFO "scull_exit\n");
    cdev_del(&scull_pipe.cdev);
    unregister_chrdev_region(MKDEV(scull_major,scull_minor),scull_nr_devs); 
    kfree(scull_pipe.buffer);
}

module_init(scull_init);
module_exit(scull_exit);

//**************************************************************************

上面贴出来的代码就是供阅读《LINUX设备驱动程序》的朋友参考一下,我的系统是UBUNTU12.04的版本,下边的MAKEFILE文件也贴出来了:

obj-m := scull.o
kernel_dir = /usr/src/linux-headers-3.2.0-29-generic-pae
all:
    make -C $(kernel_dir) M=`pwd` modules

clean:
    rm -rf *.ko *.mod.c *.o *.order *.symvers

 

posted @ 2013-02-19 16:08  printf_NSLog  阅读(624)  评论(0编辑  收藏  举报