1/*
2 * Copyright 2001-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include <stdlib.h>
7
8#include "AreaSupport.h"
9#include "Compatibility.h"
10#include "Debug.h"
11#include "Port.h"
12#include "RequestAllocator.h"
13
14
15// constructor
16RequestAllocator::RequestAllocator(Port* port)
17	:
18	fError(B_NO_INIT),
19	fPort(NULL),
20	fRequest(NULL),
21	fRequestSize(0),
22	fPortReservedOffset(0),
23	fAllocatedAreaCount(0),
24	fDeferredInitInfoCount(0),
25	fRequestInPortBuffer(false)
26{
27	Init(port);
28}
29
30// destructor
31RequestAllocator::~RequestAllocator()
32{
33	Uninit();
34}
35
36// Init
37status_t
38RequestAllocator::Init(Port* port)
39{
40	Uninit();
41	if (port) {
42		fPort = port;
43		fError = fPort->InitCheck();
44		fPortReservedOffset = fPort->ReservedSize();
45	}
46	return fError;
47}
48
49// Uninit
50void
51RequestAllocator::Uninit()
52{
53	if (fRequestInPortBuffer)
54		fPort->Unreserve(fPortReservedOffset);
55	else
56		free(fRequest);
57
58	for (int32 i = 0; i < fAllocatedAreaCount; i++)
59		delete_area(fAllocatedAreas[i]);
60	fAllocatedAreaCount = 0;
61
62	for (int32 i = 0; i < fDeferredInitInfoCount; i++) {
63		if (fDeferredInitInfos[i].inPortBuffer)
64			free(fDeferredInitInfos[i].data);
65	}
66
67	fDeferredInitInfoCount = 0;
68	fError = B_NO_INIT;
69	fPort = NULL;
70	fRequest = NULL;
71	fRequestSize = 0;
72	fPortReservedOffset = 0;
73}
74
75// Error
76status_t
77RequestAllocator::Error() const
78{
79	return fError;
80}
81
82// FinishDeferredInit
83void
84RequestAllocator::FinishDeferredInit()
85{
86	if (fError != B_OK)
87		return;
88	for (int32 i = 0; i < fDeferredInitInfoCount; i++) {
89		DeferredInitInfo& info = fDeferredInitInfos[i];
90		if (info.inPortBuffer) {
91			if (info.size > 0)
92				memcpy((uint8*)fRequest + info.offset, info.data, info.size);
93			free(info.data);
94		}
95		PRINT(("RequestAllocator::FinishDeferredInit(): area: %ld, "
96			"offset: %ld, size: %ld\n", info.area, info.offset, info.size));
97		info.target->SetTo(info.area, info.offset, info.size);
98	}
99	fDeferredInitInfoCount = 0;
100}
101
102// AllocateRequest
103status_t
104RequestAllocator::AllocateRequest(int32 size)
105{
106	if (fError != B_OK)
107		RETURN_ERROR(fError);
108
109	fRequestOffset = (fPortReservedOffset + 7) / 8 * 8;
110
111	if (size < (int32)sizeof(Request)
112		|| fRequestOffset + size > fPort->GetCapacity()) {
113		RETURN_ERROR(fError = B_BAD_VALUE);
114	}
115
116	fRequest = (Request*)((uint8*)fPort->GetBuffer() + fRequestOffset);
117	fRequestSize = size;
118	fRequestInPortBuffer = true;
119	fPort->Reserve(fRequestOffset + fRequestSize);
120	return B_OK;
121}
122
123// ReadRequest
124status_t
125RequestAllocator::ReadRequest(bigtime_t timeout)
126{
127	if (fError != B_OK)
128		RETURN_ERROR(fError);
129
130	// read the message from the port
131	void* message;
132	size_t messageSize;
133	status_t error = fPort->Receive(&message, &messageSize, timeout);
134	if (error != B_OK) {
135		if (error != B_TIMED_OUT && error != B_WOULD_BLOCK)
136			RETURN_ERROR(fError = error);
137		return error;
138	}
139
140	// shouldn't be shorter than the base Request
141	if (messageSize < (int32)sizeof(Request)) {
142		free(message);
143		RETURN_ERROR(fError = B_BAD_DATA);
144	}
145
146	// init the request
147	fRequest = (Request*)message;
148	fRequestOffset = 0;
149	fRequestSize = messageSize;
150	fRequestInPortBuffer = false;
151
152	// relocate the request
153	fError = relocate_request(fRequest, fRequestSize, fAllocatedAreas,
154		&fAllocatedAreaCount);
155	RETURN_ERROR(fError);
156}
157
158// GetRequest
159Request*
160RequestAllocator::GetRequest() const
161{
162	return fRequest;
163}
164
165// GetRequestSize
166int32
167RequestAllocator::GetRequestSize() const
168{
169	return fRequestSize;
170}
171
172// AllocateAddress
173status_t
174RequestAllocator::AllocateAddress(Address& address, int32 size, int32 align,
175	void** data, bool deferredInit)
176{
177	if (fError != B_OK)
178		return fError;
179	if (!fRequest)
180		RETURN_ERROR(B_NO_INIT);
181	if (size < 0)
182		RETURN_ERROR(B_BAD_VALUE);
183	if (fDeferredInitInfoCount >= MAX_REQUEST_ADDRESS_COUNT)
184		RETURN_ERROR(B_BAD_VALUE);
185	// fix the alignment -- valid is 1, 2, 4, 8
186	if (align <= 0 || size == 0 || (align & 0x1))
187		align = 1;
188	else if (align & 0x2)
189		align = 2;
190	else if (align & 0x4)
191		align = 4;
192	else
193		align = 8;
194	// check address location
195	// Currently we only support relocation of addresses inside the
196	// port buffer.
197	int32 addressOffset = (uint8*)&address - (uint8*)fRequest;
198	if (addressOffset < (int32)sizeof(Request)
199		|| addressOffset + (int32)sizeof(Address) > fRequestSize) {
200		RETURN_ERROR(B_BAD_VALUE);
201	}
202	// get the next free aligned offset in the port buffer
203	int32 offset = (fRequestSize + align - 1) / align * align;
204	// allocate the data
205	if (fRequestOffset + offset + size <= fPort->GetCapacity()) {
206		// there's enough free space in the port buffer
207		fRequestSize = offset + size;
208		fPort->Reserve(fRequestOffset + fRequestSize);
209		if (deferredInit) {
210			DeferredInitInfo& info
211				= fDeferredInitInfos[fDeferredInitInfoCount];
212			if (size > 0) {
213				info.data = (uint8*)malloc(size);
214				if (!info.data)
215					RETURN_ERROR(B_NO_MEMORY);
216			} else
217				info.data = NULL;
218			info.area = -1;
219			info.offset = offset;
220			info.size = size;
221			info.inPortBuffer = true;
222			info.target = &address;
223			*data = info.data;
224			fDeferredInitInfoCount++;
225		} else {
226			*data = (uint8*)fRequest + offset;
227			address.SetTo(-1, offset, size);
228		}
229	} else {
230		// not enough room in the port's buffer: we need to allocate an area
231		if (fAllocatedAreaCount >= MAX_REQUEST_ADDRESS_COUNT)
232			RETURN_ERROR(B_ERROR);
233		int32 areaSize = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE * B_PAGE_SIZE;
234		area_id area = create_area("request data", data,
235#ifdef _KERNEL_MODE
236			B_ANY_KERNEL_ADDRESS,
237#else
238			B_ANY_ADDRESS,
239#endif
240			areaSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
241		if (area < 0)
242			RETURN_ERROR(area);
243		fAllocatedAreas[fAllocatedAreaCount++] = area;
244		if (deferredInit) {
245			DeferredInitInfo& info
246				= fDeferredInitInfos[fDeferredInitInfoCount];
247			info.data = NULL;
248			info.area = area;
249			info.offset = 0;
250			info.size = size;
251			info.inPortBuffer = false;
252			info.target = &address;
253			fDeferredInitInfoCount++;
254PRINT(("  RequestAllocator::AllocateAddress(): deferred allocated area: "
255"%ld, size: %ld (%ld), data: %p\n", area, size, areaSize, *data));
256		} else
257			address.SetTo(area, 0, size);
258	}
259	return B_OK;
260}
261
262// AllocateData
263status_t
264RequestAllocator::AllocateData(Address& address, const void* data, int32 size,
265	int32 align, bool deferredInit)
266{
267	status_t error = B_OK;
268	if (data != NULL) {
269		void* destination;
270		error = AllocateAddress(address, size, align, &destination,
271			deferredInit);
272		if (error != B_OK)
273			return error;
274		if (size > 0)
275			memcpy(destination, data, size);
276	} else
277		address.SetTo(-1, 0, 0);
278	return error;
279}
280
281// AllocateString
282status_t
283RequestAllocator::AllocateString(Address& address, const char* data,
284	bool deferredInit)
285{
286	int32 size = (data ? strlen(data) + 1 : 0);
287	return AllocateData(address, data, size, 1, deferredInit);
288}
289
290// SetAddress
291/*status_t
292RequestAllocator::SetAddress(Address& address, void* data, int32 size)
293{
294	if (fError != B_OK)
295		return fError;
296	if (!fRequest)
297		return (fError = B_NO_INIT);
298	// check address location
299	// Currently we only support relocation of addresses inside the
300	// port buffer.
301	int32 addressOffset = (uint8*)&address - (uint8*)fRequest;
302	if (addressOffset < (int32)sizeof(Request)
303		|| addressOffset + (int32)sizeof(Address) > fRequestSize) {
304		return (fError = B_BAD_VALUE);
305	}
306	// if data does itself lie within the port buffer, we store only the
307	// request relative offset
308	int32 inRequestOffset = (uint8*)data - (uint8*)fRequest;
309	if (!data) {
310		address.SetTo(-1, 0, 0);
311	} else if (inRequestOffset >= (int32)sizeof(Request)
312		&& inRequestOffset <= fRequestSize) {
313		if (inRequestOffset + size > fRequestSize)
314			return (fError = B_BAD_VALUE);
315		address.SetTo(-1, inRequestOffset, size);
316	} else {
317		// get the area and in-area offset for the address
318		area_id area;
319		int32 offset;
320		fError = get_area_for_address(data, size, &area, &offset);
321		if (fError != B_OK)
322			return fError;
323		// set the address
324		address.SetTo(area, offset, size);
325	}
326	return fError;
327}*/
328
329