struct scull_pipe {
wait_queue_head_t inp, outp;
char *buffer, *end;
char *rp, wp;
int nreaders, nwriters;
struct fasync_struct *async_queue;
struct semaphore sem;
struct cdev cdev;
};
static ssize_t scull_p_read (struct file *filp, char __user *buffer, 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_interruptible(&dev->sem);
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
if (wait_event_interruptible(&dev->inp, 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(buffer, dev->rp, count))
{
up_interruptible(&dev->sem);
return -EFAULT;
}
dev->rp += count;
if (dev->rp == dev->end)
dev->rp = dev->buffer
up_interruptible(&dev->sem);
wake_up_interruptible(&dev->outp);
return count;
}
static ssize_t scull_p_write (struct file *filp, const char __user *buffer, size_t count, loff_t *f_pos)
{
struct scull_pipe *dev = filp->private_data;
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
while (strlen(dev->buffer) && dev->rp == dev->wp)
{
up_interruptible(&dev->sem);
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
if (wait_event_interruptible(&dev->outp))
return -ERESTARTSYS;
down_interruptible(&dev->sem);
}
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));
if (copy_form_user(dev->wp, buffer, count))
{
up_interruptible(&dev->sem);
return -EFAULT;
}
dev->wp += count;
if (dev->wp == dev->end)
dev->wp = dev->buffer;
up_interruptible(&dev->sem);
wake_up_interruptible(&dev->inp);
return count;
}