2011-11-16 02:37:29 -05:00
|
|
|
/******************************************************************************
|
|
|
|
|
|
|
|
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
|
|
|
|
|
|
|
This file is part of Sortix.
|
|
|
|
|
|
|
|
Sortix 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.
|
|
|
|
|
|
|
|
Sortix 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 Sortix. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
pipe.cpp
|
|
|
|
A device with a writing end and a reading end.
|
|
|
|
|
|
|
|
******************************************************************************/
|
|
|
|
|
2012-03-21 19:52:29 -04:00
|
|
|
#include <sortix/kernel/platform.h>
|
2011-11-16 02:37:29 -05:00
|
|
|
#include <libmaxsi/error.h>
|
|
|
|
#include <libmaxsi/memory.h>
|
2012-01-18 11:10:44 -05:00
|
|
|
#include "event.h"
|
2011-11-16 02:37:29 -05:00
|
|
|
#include "thread.h"
|
|
|
|
#include "process.h"
|
|
|
|
#include "syscall.h"
|
|
|
|
#include "pipe.h"
|
|
|
|
|
|
|
|
using namespace Maxsi;
|
|
|
|
|
|
|
|
namespace Sortix
|
|
|
|
{
|
|
|
|
class DevPipeStorage : public DevStream
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
typedef Device BaseClass;
|
|
|
|
|
|
|
|
public:
|
|
|
|
DevPipeStorage(byte* buffer, size_t buffersize);
|
|
|
|
~DevPipeStorage();
|
|
|
|
|
|
|
|
private:
|
|
|
|
byte* buffer;
|
|
|
|
size_t buffersize;
|
|
|
|
size_t bufferoffset;
|
|
|
|
size_t bufferused;
|
2012-01-18 11:10:44 -05:00
|
|
|
Event readevent;
|
|
|
|
Event writeevent;
|
2012-03-01 08:31:12 -05:00
|
|
|
bool anyreading;
|
|
|
|
bool anywriting;
|
2011-11-16 02:37:29 -05:00
|
|
|
|
|
|
|
public:
|
|
|
|
virtual ssize_t Read(byte* dest, size_t count);
|
|
|
|
virtual ssize_t Write(const byte* src, size_t count);
|
|
|
|
virtual bool IsReadable();
|
|
|
|
virtual bool IsWritable();
|
|
|
|
|
2012-03-01 08:31:12 -05:00
|
|
|
public:
|
|
|
|
void NotReading();
|
|
|
|
void NotWriting();
|
|
|
|
|
2011-11-16 02:37:29 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
DevPipeStorage::DevPipeStorage(byte* buffer, size_t buffersize)
|
|
|
|
{
|
|
|
|
this->buffer = buffer;
|
|
|
|
this->buffersize = buffersize;
|
|
|
|
this->bufferoffset = 0;
|
|
|
|
this->bufferused = 0;
|
2012-03-01 08:31:12 -05:00
|
|
|
this->anyreading = true;
|
|
|
|
this->anywriting = true;
|
2011-11-16 02:37:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
DevPipeStorage::~DevPipeStorage()
|
|
|
|
{
|
|
|
|
delete[] buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DevPipeStorage::IsReadable() { return true; }
|
|
|
|
bool DevPipeStorage::IsWritable() { return true; }
|
|
|
|
|
|
|
|
ssize_t DevPipeStorage::Read(byte* dest, size_t count)
|
|
|
|
{
|
|
|
|
if ( count == 0 ) { return 0; }
|
|
|
|
if ( bufferused )
|
|
|
|
{
|
|
|
|
if ( bufferused < count ) { count = bufferused; }
|
|
|
|
size_t amount = count;
|
2012-03-18 11:14:26 -04:00
|
|
|
size_t linear = buffersize - bufferoffset;
|
2011-11-16 02:37:29 -05:00
|
|
|
if ( linear < amount ) { amount = linear; }
|
2012-03-18 11:14:26 -04:00
|
|
|
ASSERT(amount);
|
2011-11-16 02:37:29 -05:00
|
|
|
Memory::Copy(dest, buffer + bufferoffset, amount);
|
|
|
|
bufferoffset = (bufferoffset + amount) % buffersize;
|
|
|
|
bufferused -= amount;
|
2012-01-18 11:10:44 -05:00
|
|
|
writeevent.Signal();
|
2012-03-18 11:14:26 -04:00
|
|
|
return amount;
|
2011-11-16 02:37:29 -05:00
|
|
|
}
|
|
|
|
|
2012-03-01 08:31:12 -05:00
|
|
|
if ( !anywriting ) { return 0; }
|
|
|
|
|
2012-01-22 12:49:04 -05:00
|
|
|
Error::Set(EBLOCKING);
|
2012-01-18 11:10:44 -05:00
|
|
|
readevent.Register();
|
2011-11-16 02:37:29 -05:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t DevPipeStorage::Write(const byte* src, size_t count)
|
|
|
|
{
|
2012-03-01 08:31:12 -05:00
|
|
|
if ( !anyreading ) { /* TODO: SIGPIPE */ }
|
2011-11-16 02:37:29 -05:00
|
|
|
if ( count == 0 ) { return 0; }
|
|
|
|
if ( bufferused < buffersize )
|
|
|
|
{
|
|
|
|
if ( buffersize - bufferused < count ) { count = buffersize - bufferused; }
|
|
|
|
size_t writeoffset = (bufferoffset + bufferused) % buffersize;
|
|
|
|
size_t amount = count;
|
|
|
|
size_t linear = buffersize - writeoffset;
|
|
|
|
if ( linear < amount ) { amount = linear; }
|
2012-03-18 11:14:26 -04:00
|
|
|
ASSERT(amount);
|
2011-11-16 02:37:29 -05:00
|
|
|
Memory::Copy(buffer + writeoffset, src, amount);
|
|
|
|
bufferused += amount;
|
2012-01-18 11:10:44 -05:00
|
|
|
readevent.Signal();
|
2012-03-18 11:14:26 -04:00
|
|
|
return amount;
|
2011-11-16 02:37:29 -05:00
|
|
|
}
|
|
|
|
|
2012-01-22 12:49:04 -05:00
|
|
|
Error::Set(EBLOCKING);
|
2012-01-18 11:10:44 -05:00
|
|
|
writeevent.Register();
|
2011-11-16 02:37:29 -05:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-03-01 08:31:12 -05:00
|
|
|
void DevPipeStorage::NotReading() { anyreading = false; }
|
|
|
|
void DevPipeStorage::NotWriting() { anywriting = false; }
|
|
|
|
|
2011-11-16 02:37:29 -05:00
|
|
|
class DevPipeReading : public DevStream
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
typedef Device BaseClass;
|
|
|
|
|
|
|
|
public:
|
|
|
|
DevPipeReading(DevStream* stream);
|
|
|
|
~DevPipeReading();
|
|
|
|
|
|
|
|
private:
|
|
|
|
DevStream* stream;
|
|
|
|
|
|
|
|
public:
|
|
|
|
virtual ssize_t Read(byte* dest, size_t count);
|
|
|
|
virtual ssize_t Write(const byte* src, size_t count);
|
|
|
|
virtual bool IsReadable();
|
|
|
|
virtual bool IsWritable();
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
DevPipeReading::DevPipeReading(DevStream* stream)
|
|
|
|
{
|
|
|
|
stream->Refer();
|
|
|
|
this->stream = stream;
|
|
|
|
}
|
|
|
|
|
|
|
|
DevPipeReading::~DevPipeReading()
|
|
|
|
{
|
2012-03-01 08:31:12 -05:00
|
|
|
((DevPipeStorage*) stream)->NotReading();
|
2011-11-16 02:37:29 -05:00
|
|
|
stream->Unref();
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t DevPipeReading::Read(byte* dest, size_t count)
|
|
|
|
{
|
|
|
|
return stream->Read(dest, count);
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t DevPipeReading::Write(const byte* /*src*/, size_t /*count*/)
|
|
|
|
{
|
2011-11-22 11:26:47 -05:00
|
|
|
Error::Set(EBADF);
|
2011-11-16 02:37:29 -05:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DevPipeReading::IsReadable()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DevPipeReading::IsWritable()
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
class DevPipeWriting : public DevStream
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
typedef Device BaseClass;
|
|
|
|
|
|
|
|
public:
|
|
|
|
DevPipeWriting(DevStream* stream);
|
|
|
|
~DevPipeWriting();
|
|
|
|
|
|
|
|
private:
|
|
|
|
DevStream* stream;
|
|
|
|
|
|
|
|
public:
|
|
|
|
virtual ssize_t Read(byte* dest, size_t count);
|
|
|
|
virtual ssize_t Write(const byte* src, size_t count);
|
|
|
|
virtual bool IsReadable();
|
|
|
|
virtual bool IsWritable();
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
DevPipeWriting::DevPipeWriting(DevStream* stream)
|
|
|
|
{
|
|
|
|
stream->Refer();
|
|
|
|
this->stream = stream;
|
|
|
|
}
|
|
|
|
|
|
|
|
DevPipeWriting::~DevPipeWriting()
|
|
|
|
{
|
2012-03-01 08:31:12 -05:00
|
|
|
((DevPipeStorage*) stream)->NotWriting();
|
2011-11-16 02:37:29 -05:00
|
|
|
stream->Unref();
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t DevPipeWriting::Read(byte* /*dest*/, size_t /*count*/)
|
|
|
|
{
|
2011-11-22 11:26:47 -05:00
|
|
|
Error::Set(EBADF);
|
2011-11-16 02:37:29 -05:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t DevPipeWriting::Write(const byte* src, size_t count)
|
|
|
|
{
|
|
|
|
return stream->Write(src, count);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DevPipeWriting::IsReadable()
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DevPipeWriting::IsWritable()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace Pipe
|
|
|
|
{
|
|
|
|
const size_t BUFFER_SIZE = 4096UL;
|
|
|
|
|
|
|
|
int SysPipe(int pipefd[2])
|
|
|
|
{
|
|
|
|
// TODO: Validate that pipefd is a valid user-space array!
|
2012-03-21 19:52:29 -04:00
|
|
|
|
2011-11-16 02:37:29 -05:00
|
|
|
size_t buffersize = BUFFER_SIZE;
|
|
|
|
byte* buffer = new byte[buffersize];
|
|
|
|
if ( !buffer ) { return -1; /* TODO: ENOMEM */ }
|
|
|
|
|
|
|
|
// Transfer ownership of the buffer to the storage device.
|
|
|
|
DevStream* storage = new DevPipeStorage(buffer, buffersize);
|
|
|
|
if ( !storage ) { delete[] buffer; return -1; /* TODO: ENOMEM */ }
|
|
|
|
|
|
|
|
DevStream* reading = new DevPipeReading(storage);
|
|
|
|
if ( !reading ) { delete storage; return -1; /* TODO: ENOMEM */ }
|
|
|
|
|
|
|
|
DevStream* writing = new DevPipeWriting(storage);
|
|
|
|
if ( !writing ) { delete reading; return -1; /* TODO: ENOMEM */ }
|
|
|
|
|
|
|
|
Process* process = CurrentProcess();
|
|
|
|
int readfd = process->descriptors.Allocate(reading);
|
|
|
|
int writefd = process->descriptors.Allocate(writing);
|
|
|
|
|
|
|
|
if ( readfd < 0 || writefd < 0 )
|
|
|
|
{
|
|
|
|
if ( 0 <= readfd ) { process->descriptors.Free(readfd); } else { delete reading; }
|
|
|
|
if ( 0 <= writefd ) { process->descriptors.Free(writefd); } else { delete writing; }
|
|
|
|
|
2012-03-21 19:52:29 -04:00
|
|
|
return -1; /* TODO: ENOMEM/EMFILE/ENFILE */
|
2011-11-16 02:37:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
pipefd[0] = readfd;
|
|
|
|
pipefd[1] = writefd;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Init()
|
|
|
|
{
|
|
|
|
Syscall::Register(SYSCALL_PIPE, (void*) SysPipe);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|