pipe.c
上传用户:wudi5211
上传日期:2010-01-21
资源大小:607k
文件大小:14k
- /*
- * 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 sleepn", 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 bytesn",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 sleepn",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 %pn",
- (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 bytesn",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 readn");
- 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 writen");
- 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 %in", 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: %pn", i, p);
- /* len += sprintf(buf+len, " Queues: %p %pn", 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 %pn", p->rp, p->wp);
- len += sprintf(buf+len, " readers %i writers %in",
- 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. %in", 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;
- }