📄 pipe.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 + -