⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pipe.c

📁 本源码是将述嵌入式LINUX驱动程序例程
💻 C
字号:
/*
 * pipe.c -- fifo driver for scull
 *
 * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
 * Copyright (C) 2001 O'Reilly & Associates
 *
 * The source code in this file can be freely used, adapted,
 * and redistributed in source or binary form, so long as an
 * acknowledgment appears in derived source files.  The citation
 * should list that the code comes from the book "Linux Device
 * Drivers" by Alessandro Rubini and Jonathan Corbet, published
 * by O'Reilly & Associates.   No warranty is attached;
 * we cannot take responsibility for errors or fitness for use.
 *
 */
 
#ifndef __KERNEL__
#  define __KERNEL__
#endif
#ifndef MODULE
#  define MODULE
#endif

#define __NO_VERSION__
#include <linux/module.h>  /* get MOD_DEC_USE_COUNT, not the version string */
#include <linux/version.h> /* needed for the conditionals in scull.h */

#include <linux/kernel.h> /* printk() */
#include <linux/malloc.h> /* kmalloc() */
#include <linux/fs.h>     /* everything... */
#include <linux/proc_fs.h>
#include <linux/errno.h>  /* error codes */
#include <linux/types.h>  /* size_t */
#include <linux/fcntl.h>

#include "scull.h"        /* local definitions and sysdep.h */

#ifndef min
#  define min(a,b) ((a)<(b) ? (a) : (b)) /* we use it in this file */
#endif

typedef struct Scull_Pipe {
    wait_queue_head_t inq, outq;    /* read and write queues */
    char *buffer, *end;             /* begin of buf, end of buf */
    int buffersize;                 /* used in pointer arithmetic */
    char *rp, *wp;                  /* where to read, where to write */
    int nreaders, nwriters;         /* number of openings for r/w */
    struct fasync_struct *async_queue; /* asynchronous readers */
    struct semaphore sem;           /* mutual exclusion semaphore */
    devfs_handle_t handle;         /* only used if devfs is there */
} Scull_Pipe;

/* parameters */
int scull_p_nr_devs = SCULL_P_NR_DEVS;  /* number of pipe devices */
int scull_p_buffer =  SCULL_P_BUFFER;   /* buffer size */

MODULE_PARM(scull_p_nr_devs,"i");
MODULE_PARM(scull_p_buffer,"i");

Scull_Pipe *scull_p_devices;


/*
 * Open and close
 */

int scull_p_open(struct inode *inode, struct file *filp)
{
    Scull_Pipe *dev;
    int num = NUM(inode->i_rdev);

    if (!filp->private_data) {
        if (num >= scull_p_nr_devs) return -ENODEV;
        dev = &scull_p_devices[num];
        filp->private_data = dev;
    } else {
        dev = filp->private_data;
    }
    if (down_interruptible(&dev->sem))
        return -ERESTARTSYS;
    if (!dev->buffer) { /* allocate the buffer */
        dev->buffer = kmalloc(scull_p_buffer, GFP_KERNEL);
        if (!dev->buffer) {
            up(&dev->sem);
            return -ENOMEM;
        }
    }
    dev->buffersize = scull_p_buffer;
    dev->end = dev->buffer + dev->buffersize;
    dev->rp = dev->wp = dev->buffer; /* rd and wr from the beginning */

    /* use f_mode,not  f_flags: it's cleaner (fs/open.c tells why) */
    if (filp->f_mode & FMODE_READ)
        dev->nreaders++;
    if (filp->f_mode & FMODE_WRITE)
        dev->nwriters++;
    up(&dev->sem);
    
    filp->private_data = dev;
    MOD_INC_USE_COUNT;
    return 0;
}

int scull_p_release(struct inode *inode, struct file *filp)
{
    Scull_Pipe *dev = filp->private_data;
    int scull_p_fasync(fasync_file fd, struct file *filp, int mode);

#ifdef LINUX_20
    scull_p_fasync(inode, filp, 0);
#else
    /* remove this filp from the asynchronously notified filp's */
    scull_p_fasync(-1, filp, 0);
#endif
    down(&dev->sem);
    if (filp->f_mode & FMODE_READ)
        dev->nreaders--;
    if (filp->f_mode & FMODE_WRITE)
        dev->nwriters--;
    if (dev->nreaders + dev->nwriters == 0) {
        kfree(dev->buffer);
        dev->buffer = NULL; /* the other fields are not checked on open */
    }
    up(&dev->sem);
    MOD_DEC_USE_COUNT;
    return 0;
}

/*
 * Data management: read and write
 */

ssize_t scull_p_read (struct file *filp, char *buf, size_t count,
                loff_t *f_pos)
{
    Scull_Pipe *dev = filp->private_data;

    if (f_pos != &filp->f_pos) return -ESPIPE;

    if (down_interruptible(&dev->sem))
        return -ERESTARTSYS;
    while (dev->rp == dev->wp) { /* nothing to read */
        up(&dev->sem); /* release the lock */
        if (filp->f_flags & O_NONBLOCK)
            return -EAGAIN;
        PDEBUG("\"%s\" reading: going to sleep\n", current->comm);
        if (wait_event_interruptible(dev->inq, (dev->rp != dev->wp)))
            return -ERESTARTSYS; /* signal: tell the fs layer to handle it */
        /* otherwise loop, but first reacquire the lock */
        if (down_interruptible(&dev->sem))
            return -ERESTARTSYS;
    }
    /* ok, data is there, return something */
    if (dev->wp > dev->rp)
        count = min(count, dev->wp - dev->rp);
    else /* the write pointer has wrapped, return data up to dev->end */
        count = min(count, 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; /* wrapped */
    up (&dev->sem);

    /* finally, awake any writers and return */
    wake_up_interruptible(&dev->outq);
    PDEBUG("\"%s\" did read %li bytes\n",current->comm, (long)count);
    return count;
}

static inline int spacefree(Scull_Pipe *dev)
{
    if (dev->rp == dev->wp)
        return dev->buffersize - 1;
    return ((dev->rp + dev->buffersize - dev->wp) % dev->buffersize) - 1;
}

ssize_t scull_p_write(struct file *filp, const char *buf, size_t count,
                loff_t *f_pos)
{
    Scull_Pipe *dev = filp->private_data;
    
    if (f_pos != &filp->f_pos) return -ESPIPE;

    if (down_interruptible(&dev->sem))
        return -ERESTARTSYS;
    
    /* Make sure there's space to write */
    while (spacefree(dev) == 0) { /* full */
        up(&dev->sem);
        if (filp->f_flags & O_NONBLOCK)
            return -EAGAIN;
        PDEBUG("\"%s\" writing: going to sleep\n",current->comm);
        if (wait_event_interruptible(dev->outq, spacefree(dev) > 0))
            return -ERESTARTSYS; /* signal: tell the fs layer to handle it */
        if (down_interruptible(&dev->sem))
            return -ERESTARTSYS;
    }
    /* ok, space is there, accept something */
    count = min(count, spacefree(dev));
    if (dev->wp >= dev->rp)
        count = min(count, dev->end - dev->wp); /* up to end-of-buffer */
    else /* the write pointer has wrapped, fill up to rp-1 */
        count = min(count, dev->rp - dev->wp - 1);
    PDEBUG("Going to accept %li bytes to %p from %p\n",
           (long)count, dev->wp, buf);
    if (copy_from_user(dev->wp, buf, count)) {
        up (&dev->sem);
        return -EFAULT;
    }
    dev->wp += count;
    if (dev->wp == dev->end)
        dev->wp = dev->buffer; /* wrapped */
    up(&dev->sem);

    /* finally, awake any reader */
    wake_up_interruptible(&dev->inq);  /* blocked in read() and select() */

    /* and signal asynchronous readers, explained late in chapter 5 */
    if (dev->async_queue)
        kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
    PDEBUG("\"%s\" did write %li bytes\n",current->comm, (long)count);
    return count;
}

#ifdef __USE_OLD_SELECT__
int scull_p_poll(struct inode *inode, struct file *filp,
                  int mode, select_table *table)
{
    Scull_Pipe *dev = filp->private_data;

    if (mode == SEL_IN) {
        if (dev->rp != dev->wp) return 1; /* readable */
        PDEBUG("Waiting to read\n");
        select_wait(&dev->inq, table); /* wait for data */
        return 0;
    }
    if (mode == SEL_OUT) {
        /*
         * The buffer is circular; it is considered full
         * if "wp" is right behind "rp". "left" is 0 if the
         * buffer is empty, and it is "1" if it is completely full.
         */
        int left = (dev->rp + dev->buffersize - dev->wp) % dev->buffersize;
        if (left != 1) return 1; /* writable */
        PDEBUG("Waiting to write\n");
        select_wait(&dev->outq, table); /* wait for free space */
        return 0;
    }
    return 0; /* never exception-able */
}
#else /* Use poll instead, already shown */

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

    /*
     * The buffer is circular; it is considered full
     * if "wp" is right behind "rp". "left" is 0 if the
     * buffer is empty, and it is "1" if it is completely full.
     */
    int left = (dev->rp + dev->buffersize - dev->wp) % dev->buffersize;

    poll_wait(filp, &dev->inq,  wait);
    poll_wait(filp, &dev->outq, wait);
    if (dev->rp != dev->wp) mask |= POLLIN | POLLRDNORM;  /* readable */
    if (left != 1)          mask |= POLLOUT | POLLWRNORM; /* writable */

    return mask;
}

#endif




int scull_p_fasync(fasync_file fd, struct file *filp, int mode)
{
    Scull_Pipe *dev = filp->private_data;

    return fasync_helper(fd, filp, mode, &dev->async_queue);
}


loff_t scull_p_llseek(struct file *filp,  loff_t off, int whence)
{
    return -ESPIPE; /* unseekable */
}

#ifdef SCULL_DEBUG
void scullp_proc_offset(char *buf, char **start, off_t *offset, int *len)
{
    if (*offset == 0)
        return;
    if (*offset >= *len) {      /* Not there yet */
        *offset -= *len;
        *len = 0;
    }
    else {                      /* We're into the interesting stuff now */
        *start = buf + *offset;
        *offset = 0;
    }
}


int scull_read_p_mem(char *buf, char **start, off_t offset,
                   int count, int *eof, void *data)
{
    int i, len;
    Scull_Pipe *p;

    #define LIMIT (PAGE_SIZE-200) /* don't print any more after this size */
    *start = buf;
    len = sprintf(buf, "Default buffersize is %i\n", scull_p_buffer);
    for(i = 0; i<scull_p_nr_devs && len <= LIMIT; i++) {
        p = &scull_p_devices[i];
        if (down_interruptible(&p->sem))
            return -ERESTARTSYS;
        len += sprintf(buf+len, "\nDevice %i: %p\n", i, p);
/*        len += sprintf(buf+len, "   Queues: %p %p\n", p->inq, p->outq);*/
        len += sprintf(buf+len, "   Buffer: %p to %p (%i bytes)\n",
                       p->buffer, p->end, p->buffersize);
        len += sprintf(buf+len, "   rp %p   wp %p\n", p->rp, p->wp);
        len += sprintf(buf+len, "   readers %i   writers %i\n",
                       p->nreaders, p->nwriters);
        up(&p->sem);
        scullp_proc_offset(buf, start, &offset, &len);
    }
    *eof = (len <= LIMIT);
    return len;
}


#ifdef USE_PROC_REGISTER

static int scull_p_get_info(char *buf, char **start, off_t offset, int len,
                int unused)
{
    int eof = 0;
    return scull_read_p_mem(buf, start, offset, len, &eof, NULL);
}

struct proc_dir_entry scull_proc_p_entry = {
        0,                  /* low_ino: the inode -- dynamic */
        9, "scullpipe",     /* len of name and name */
        S_IFREG | S_IRUGO,  /* mode */
        1, 0, 0,            /* nlinks, owner, group */
        0, NULL,            /* size - unused; operations -- use default */
        &scull_p_get_info,  /* function used to read data */
        /* nothing more */
    };

static inline void create_proc_read_entry(const char *name, mode_t mode,
                struct proc_dir_entry *base, void *read_func, void *data)
{
    proc_register_dynamic(&proc_root, &scull_proc_p_entry);
}

static inline void remove_proc_entry(char *name, void *parent)
{
    proc_unregister(&proc_root, scull_proc_p_entry.low_ino);
}

#endif /* USE_PROC_REGISTER */

#endif


/*
 * 2.0 wrappers
 */
#ifdef LINUX_20

static int scull_p_lseek_20(struct inode *ino, struct file *f,
                off_t offset, int whence)
{
    return (int)scull_p_llseek(f, offset, whence);
}

int scull_p_read_20(struct inode *ino, struct file *f, char *buf, int count)
{
    return (int)scull_p_read(f, buf, count, &f->f_pos);
}

int scull_p_write_20(struct inode *ino, struct file *f, const char *b, int c)
{
    return (int)scull_p_write(f, b, c, &f->f_pos);
}

void scull_p_release_20(struct inode *ino, struct file *f)
{
    scull_p_release(ino, f);
}

#define scull_p_llseek scull_p_lseek_20
#define scull_p_read scull_p_read_20
#define scull_p_write scull_p_write_20
#define scull_p_release scull_p_release_20
#define llseek lseek
#define poll select
#endif


/*
 * The file operations for the pipe device
 * (some are overlayed with bare scull)
 */
struct file_operations scull_pipe_fops = {
    llseek:     scull_p_llseek,
    read:       scull_p_read,
    write:      scull_p_write,
    poll:       scull_p_poll,
    ioctl:      scull_ioctl,
    open:       scull_p_open,
    release:    scull_p_release,
    fasync:     scull_p_fasync,
};


char pipename[8]; /* only used for devfs names */

int scull_p_init(void)
{
    int i;

    SET_MODULE_OWNER(&scull_pipe_fops);
    scull_p_devices = kmalloc(scull_p_nr_devs * sizeof(Scull_Pipe),
                              GFP_KERNEL);
    if (scull_p_devices == NULL)
        return -ENOMEM;
    memset(scull_p_devices, 0, scull_p_nr_devs * sizeof(Scull_Pipe));
    for (i = 0; i < scull_p_nr_devs; i++) {
        init_waitqueue_head(&(scull_p_devices[i].inq));
        init_waitqueue_head(&(scull_p_devices[i].outq));
        sema_init(&scull_p_devices[i].sem, 1);
#ifdef CONFIG_DEVFS_FS
        sprintf(pipename, "pipe%i", i);
        scull_p_devices[i].handle =
            devfs_register(scull_devfs_dir, pipename,
                           DEVFS_FL_AUTO_DEVNUM,
                           0, 0, S_IFCHR | S_IRUGO | S_IWUGO,
                           &scull_pipe_fops,
                           scull_p_devices + i);
        if (!scull_p_devices[i].handle) {
            /* ignore errors, at worse we have less devices */
            printk(KERN_WARNING
                   "scull: can't register pipe device nr. %i\n", i);
        }
#endif
    }
#ifdef SCULL_DEBUG
    create_proc_read_entry("scullpipe", 0, NULL, scull_read_p_mem, NULL);
#endif
    return 0;
}

/*
 * This is called by cleanup_module or on failure.
 * It is required to never fail, even if nothing was initialized first
 */
void scull_p_cleanup(void)
{
    int i;

#ifdef SCULL_DEBUG
    remove_proc_entry("scullpipe", 0);
#endif

    if (!scull_p_devices) return; /* nothing else to release */
    for (i=0; i < scull_p_nr_devs; i++) {
        if (scull_p_devices[i].buffer)
            kfree(scull_p_devices[i].buffer);
        devfs_unregister(scull_p_devices[i].handle);
    }
    kfree(scull_p_devices);
    scull_p_devices = NULL; /* pedantic */
    return;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -