/* Copyright (C) 2008 Emmanuel Varoquaux
This file is part of XOS.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#include "file_struct.h"
#include "file_table.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* Operations */
static int pipe_read(struct file_struct *pipe, unsigned long pos, char *fs_buf, unsigned int count)
{
int n;
if (!count)
return 0;
if (!verify_area(fs_buf, count, PF_WRITE))
return -EFAULT;
pos &= PIPE_BUFFER_SIZE - 1;
n = 0;
while (!pipe->size) {
if (!pipe->write_count)
return 0;
if (!condition_wait_interruptible(&pipe->condition))
return -ERESTARTSYS;
}
do {
put_fs_byte(pipe->buffer[pos], fs_buf++);
pos = (pos + 1) & (PIPE_BUFFER_SIZE - 1);
pipe->size--;
n++;
count--;
}
while (count && pipe->size);
condition_signal_all(&pipe->condition);
return n;
}
static int pipe_write(struct file_struct *pipe, unsigned long pos, const char *fs_buf, unsigned int count)
{
int n;
if (!count)
return 0;
if (!verify_area(fs_buf, count, PF_READ))
return -EFAULT;
pos &= PIPE_BUFFER_SIZE - 1;
n = 0;
if (!pipe->read_count) {
signal_self(SIGPIPE);
return -EPIPE;
}
do {
while (pipe->size == PIPE_BUFFER_SIZE) {
if (!pipe->read_count) {
signal_self(SIGPIPE);
return n ? : -EPIPE;
}
if (!condition_wait_interruptible(&pipe->condition))
if (!n)
return -ERESTARTSYS;
}
do {
pipe->buffer[pos] = get_fs_byte(fs_buf++);
pos = (pos + 1) & (PIPE_BUFFER_SIZE - 1);
pipe->size++;
n++;
count--;
}
while (count && pipe->size < PIPE_BUFFER_SIZE);
pipe->mtime = get_time();
condition_signal_all(&pipe->condition);
}
while (count);
return n;
}
static long pipe_seek(const struct file_struct *pipe, unsigned long pos, long offset, int whence)
{
return -ESPIPE;
}
static int pipe_stat(const struct file_struct *pipe, struct stat_struct *fs_buf)
{
struct stat_struct statbuf;
if (!verify_area(fs_buf, sizeof (struct stat_struct), PF_WRITE))
return -EFAULT;
statbuf.dev = DEV_NONE;
statbuf.id = 0;
statbuf.type = FT_PIPE;
statbuf.rdev = DEV_NONE;
statbuf.size = 0;
statbuf.mtime = pipe->mtime;
memcpy_tofs(fs_buf, &statbuf, sizeof (struct stat_struct));
return 0;
}
static int pipe_ioctl(const struct file_struct *pipe, int request, void *fs_arg)
{
return -ENOTTY;
}
static const struct file_operations_struct pipe_operations = {
.read = pipe_read,
.write = pipe_write,
.seek = pipe_seek,
.stat = pipe_stat,
.ioctl = pipe_ioctl
};
/* Initialisation / destruction */
static int init_pipe(struct file_struct *pipe)
{
pipe->file_operations = &pipe_operations;
pipe->read_count = 0;
pipe->write_count = 0;
if (!(pipe->buffer = kmalloc(PIPE_BUFFER_SIZE)))
return -ENOMEM;
pipe->size = 0;
pipe->mtime = get_time();
condition_init(&pipe->condition);
return 0;
}
static void destroy_pipe(struct file_struct *pipe)
{
kfree(pipe->buffer, PIPE_BUFFER_SIZE);
}
static void free_pipe(struct file_struct *pipe)
{
destroy_pipe(pipe);
free_file(pipe);
}
/* Fonctions exportees */
/* Alloue un tube. */
int alloc_pipe(struct file_struct **pipe)
{
int retval;
if (!(*pipe = alloc_file())) {
retval = -ENOMEM;
goto error;
}
if ((retval = init_pipe(*pipe)) < 0)
goto error_free_pipe;
return 0;
error_free_pipe:
free_file(*pipe);
error:
return retval;
}
/* Incremente le nombre de references en lecture sur le tube. */
void pipe_hold_read(struct file_struct *pipe)
{
pipe->read_count++;
}
/* Incremente le nombre de references en ecriture sur le tube. */
void pipe_hold_write(struct file_struct *pipe)
{
pipe->write_count++;
}
/* Decremente le nombre de references en lecture sur le tube.
* Le fichier est libere s'il n'est plus reference. */
void pipe_release_read(struct file_struct *pipe)
{
pipe->read_count--;
if (!pipe->read_count && !pipe->write_count) /* liberable ? */
free_pipe(pipe);
else
condition_signal_all(&pipe->condition);
}
/* Decremente le nombre de references en ecriture sur le tube.
* Le fichier est libere s'il n'est plus reference. */
void pipe_release_write(struct file_struct *pipe)
{
pipe->write_count--;
if (!pipe->read_count && !pipe->write_count) /* liberable ? */
free_pipe(pipe);
else
condition_signal_all(&pipe->condition);
}