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

📄 pipe.c

📁 Linux Device Drivers 2nd 经典书籍的配套源码
💻 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 */#endiftypedef 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;}#endifint 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_DEBUGvoid 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_REGISTERstatic 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_20static 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 + -