* Copyright 2001-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include <stdlib.h>
#include <KernelExport.h>
#include <kernel.h>
#include "AreaSupport.h"
#include "Compatibility.h"
#include "Debug.h"
#include "Port.h"
#include "RequestAllocator.h"
RequestAllocator::RequestAllocator(Port* port)
:
fError(B_NO_INIT),
fPort(NULL),
fRequest(NULL),
fRequestSize(0),
fPortReservedOffset(0),
fAllocatedAreaCount(0),
fDeferredInitInfoCount(0),
fRequestInPortBuffer(false)
{
Init(port);
}
RequestAllocator::~RequestAllocator()
{
Uninit();
}
status_t
RequestAllocator::Init(Port* port)
{
Uninit();
if (port) {
fPort = port;
fError = fPort->InitCheck();
fPortReservedOffset = fPort->ReservedSize();
}
return fError;
}
void
RequestAllocator::Uninit()
{
if (fRequestInPortBuffer)
fPort->Unreserve(fPortReservedOffset);
else
free(fRequest);
for (int32 i = 0; i < fAllocatedAreaCount; i++)
delete_area(fAllocatedAreas[i]);
fAllocatedAreaCount = 0;
for (int32 i = 0; i < fDeferredInitInfoCount; i++) {
if (fDeferredInitInfos[i].inPortBuffer)
free(fDeferredInitInfos[i].data);
}
fDeferredInitInfoCount = 0;
fError = B_NO_INIT;
fPort = NULL;
fRequest = NULL;
fRequestSize = 0;
fPortReservedOffset = 0;
}
status_t
RequestAllocator::Error() const
{
return fError;
}
void
RequestAllocator::FinishDeferredInit()
{
if (fError != B_OK)
return;
for (int32 i = 0; i < fDeferredInitInfoCount; i++) {
DeferredInitInfo& info = fDeferredInitInfos[i];
if (info.inPortBuffer) {
if (info.size > 0)
memcpy((uint8*)fRequest + info.offset, info.data, info.size);
free(info.data);
}
PRINT(("RequestAllocator::FinishDeferredInit(): area: %" B_PRId32 ", "
"offset: %" B_PRId32 ", size: %" B_PRId32 "\n", info.area,
info.offset, info.size));
info.target->SetTo(info.area, info.offset, info.size);
}
fDeferredInitInfoCount = 0;
}
status_t
RequestAllocator::AllocateRequest(int32 size)
{
if (fError != B_OK)
RETURN_ERROR(fError);
fRequestOffset = (fPortReservedOffset + 7) / 8 * 8;
if (size < (int32)sizeof(Request)
|| fRequestOffset + size > fPort->GetCapacity()) {
RETURN_ERROR(fError = B_BAD_VALUE);
}
fRequest = (Request*)((uint8*)fPort->GetBuffer() + fRequestOffset);
fRequestSize = size;
fRequestInPortBuffer = true;
fPort->Reserve(fRequestOffset + fRequestSize);
return B_OK;
}
status_t
RequestAllocator::ReadRequest(bigtime_t timeout)
{
if (fError != B_OK)
RETURN_ERROR(fError);
void* message;
size_t messageSize;
status_t error = fPort->Receive(&message, &messageSize, timeout);
if (error != B_OK) {
if (error != B_TIMED_OUT && error != B_WOULD_BLOCK)
RETURN_ERROR(fError = error);
return error;
}
if (messageSize < (int32)sizeof(Request)) {
free(message);
RETURN_ERROR(fError = B_BAD_DATA);
}
fRequest = (Request*)message;
fRequestOffset = 0;
fRequestSize = messageSize;
fRequestInPortBuffer = false;
fError = relocate_request(fRequest, fRequestSize, fAllocatedAreas,
&fAllocatedAreaCount);
RETURN_ERROR(fError);
}
Request*
RequestAllocator::GetRequest() const
{
return fRequest;
}
int32
RequestAllocator::GetRequestSize() const
{
return fRequestSize;
}
status_t
RequestAllocator::AllocateAddress(Address& address, int32 size, int32 align,
void** data, bool deferredInit, int32 reserveForNextRequests)
{
if (fError != B_OK)
return fError;
if (!fRequest)
RETURN_ERROR(B_NO_INIT);
if (size < 0)
RETURN_ERROR(B_BAD_VALUE);
if (fDeferredInitInfoCount >= MAX_REQUEST_ADDRESS_COUNT)
RETURN_ERROR(B_BAD_VALUE);
if (align <= 0 || size == 0 || (align & 0x1))
align = 1;
else if (align & 0x2)
align = 2;
else if (align & 0x4)
align = 4;
else
align = 8;
int32 addressOffset = (uint8*)&address - (uint8*)fRequest;
if (addressOffset < (int32)sizeof(Request)
|| addressOffset + (int32)sizeof(Address) > fRequestSize) {
RETURN_ERROR(B_BAD_VALUE);
}
int32 offset = (fRequestSize + align - 1) / align * align;
if (fRequestOffset + offset + size + reserveForNextRequests <= fPort->GetCapacity()) {
fRequestSize = offset + size;
fPort->Reserve(fRequestOffset + fRequestSize);
if (deferredInit) {
DeferredInitInfo& info
= fDeferredInitInfos[fDeferredInitInfoCount];
if (size > 0) {
info.data = (uint8*)malloc(size);
if (!info.data)
RETURN_ERROR(B_NO_MEMORY);
} else
info.data = NULL;
info.area = -1;
info.offset = offset;
info.size = size;
info.inPortBuffer = true;
info.target = &address;
*data = info.data;
fDeferredInitInfoCount++;
} else {
*data = (uint8*)fRequest + offset;
address.SetTo(-1, offset, size);
}
} else {
if (fAllocatedAreaCount >= MAX_REQUEST_ADDRESS_COUNT)
RETURN_ERROR(B_ERROR);
int32 areaSize = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE * B_PAGE_SIZE;
area_id area = create_area("request data", data,
#ifdef _KERNEL_MODE
B_ANY_KERNEL_ADDRESS,
#else
B_ANY_ADDRESS,
#endif
areaSize, B_NO_LOCK,
#ifdef _KERNEL_MODE
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA
#else
B_READ_AREA | B_WRITE_AREA
#endif
);
if (area < 0)
RETURN_ERROR(area);
fAllocatedAreas[fAllocatedAreaCount++] = area;
if (deferredInit) {
DeferredInitInfo& info
= fDeferredInitInfos[fDeferredInitInfoCount];
info.data = NULL;
info.area = area;
info.offset = 0;
info.size = size;
info.inPortBuffer = false;
info.target = &address;
fDeferredInitInfoCount++;
PRINT((" RequestAllocator::AllocateAddress(): deferred allocated "
"area: %" B_PRId32 ", size: %" B_PRId32 " (%" B_PRId32
"), data: %p\n", area, size, areaSize, *data));
} else
address.SetTo(area, 0, size);
}
return B_OK;
}
status_t
RequestAllocator::AllocateData(Address& address, const void* data, int32 size,
int32 align, bool deferredInit, int32 reserveForNextRequests)
{
status_t error = B_OK;
if (data != NULL) {
void* destination;
error = AllocateAddress(address, size, align, &destination,
deferredInit, reserveForNextRequests);
if (error != B_OK)
return error;
if (size > 0) {
#ifndef _KERNEL_MODE
memcpy(destination, data, size);
#else
if (user_memcpy(destination, data, size) < B_OK)
return B_BAD_ADDRESS;
#endif
}
} else
address.SetTo(-1, 0, 0);
return error;
}
status_t
RequestAllocator::AllocateString(Address& address, const char* data,
bool deferredInit)
{
int32 size = (data ? strlen(data) + 1 : 0);
return AllocateData(address, data, size, 1, deferredInit);
}
RequestAllocator::SetAddress(Address& address, void* data, int32 size)
{
if (fError != B_OK)
return fError;
if (!fRequest)
return (fError = B_NO_INIT);
// check address location
// Currently we only support relocation of addresses inside the
// port buffer.
int32 addressOffset = (uint8*)&address - (uint8*)fRequest;
if (addressOffset < (int32)sizeof(Request)
|| addressOffset + (int32)sizeof(Address) > fRequestSize) {
return (fError = B_BAD_VALUE);
}
// if data does itself lie within the port buffer, we store only the
// request relative offset
int32 inRequestOffset = (uint8*)data - (uint8*)fRequest;
if (!data) {
address.SetTo(-1, 0, 0);
} else if (inRequestOffset >= (int32)sizeof(Request)
&& inRequestOffset <= fRequestSize) {
if (inRequestOffset + size > fRequestSize)
return (fError = B_BAD_VALUE);
address.SetTo(-1, inRequestOffset, size);
} else {
// get the area and in-area offset for the address
area_id area;
int32 offset;
fError = get_area_for_address(data, size, &area, &offset);
if (fError != B_OK)
return fError;
// set the address
address.SetTo(area, offset, size);
}
return fError;
}*/