1/*
2 * Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files or portions
6 * thereof (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so, subject
10 * to the following conditions:
11 *
12 *  * Redistributions of source code must retain the above copyright notice,
13 *    this list of conditions and the following disclaimer.
14 *
15 *  * Redistributions in binary form must reproduce the above copyright notice
16 *    in the  binary, as well as this list of conditions and the following
17 *    disclaimer in the documentation and/or other materials provided with
18 *    the distribution.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 * THE SOFTWARE.
27 *
28 */
29
30
31#include <BufferGroup.h>
32
33#include <Buffer.h>
34
35#include "MediaDebug.h"
36#include "DataExchange.h"
37#include "SharedBufferList.h"
38
39
40BBufferGroup::BBufferGroup(size_t size, int32 count, uint32 placement,
41	uint32 lock)
42{
43	CALLED();
44	fInitError = _Init();
45	if (fInitError != B_OK)
46		return;
47
48	// This one is easy. We need to create "count" BBuffers,
49	// each one "size" bytes large. They all go into one
50	// area, with "placement" and "lock" attributes.
51	// The BBuffers created will clone the area, and
52	// then we delete our area. This way BBuffers are
53	// independent from the BBufferGroup
54
55	// don't allow all placement parameter values
56	if (placement != B_ANY_ADDRESS && placement != B_ANY_KERNEL_ADDRESS) {
57		ERROR("BBufferGroup: placement != B_ANY_ADDRESS "
58			"&& placement != B_ANY_KERNEL_ADDRESS (0x%#" B_PRIx32 ")\n",
59			placement);
60		placement = B_ANY_ADDRESS;
61	}
62
63	// first we roundup for a better placement in memory
64	size_t allocSize = (size + 63) & ~63;
65
66	// now we create the area
67	size_t areaSize
68		= ((allocSize * count) + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
69
70	void* startAddress;
71	area_id bufferArea = create_area("some buffers area", &startAddress,
72		placement, areaSize, lock, B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA);
73	if (bufferArea < 0) {
74		ERROR("BBufferGroup: failed to allocate %ld bytes area\n", areaSize);
75		fInitError = (status_t)bufferArea;
76		return;
77	}
78
79	buffer_clone_info info;
80
81	for (int32 i = 0; i < count; i++) {
82		info.area = bufferArea;
83		info.offset = i * allocSize;
84		info.size = size;
85
86		fInitError = AddBuffer(info);
87		if (fInitError != B_OK)
88			break;
89	}
90
91	delete_area(bufferArea);
92}
93
94
95BBufferGroup::BBufferGroup()
96{
97	CALLED();
98	fInitError = _Init();
99	if (fInitError != B_OK)
100		return;
101
102	// this one simply creates an empty BBufferGroup
103}
104
105
106BBufferGroup::BBufferGroup(int32 count, const media_buffer_id* buffers)
107{
108	CALLED();
109	fInitError = _Init();
110	if (fInitError != B_OK)
111		return;
112
113	// This one creates "BBuffer"s from "media_buffer_id"s passed
114	// by the application.
115
116	buffer_clone_info info;
117
118	for (int32 i = 0; i < count; i++) {
119		info.buffer = buffers[i];
120
121		fInitError = AddBuffer(info);
122		if (fInitError != B_OK)
123			break;
124	}
125}
126
127
128BBufferGroup::~BBufferGroup()
129{
130	CALLED();
131	if (fBufferList != NULL)
132		fBufferList->DeleteGroupAndPut(fReclaimSem);
133
134	delete_sem(fReclaimSem);
135}
136
137
138status_t
139BBufferGroup::InitCheck()
140{
141	CALLED();
142	return fInitError;
143}
144
145
146status_t
147BBufferGroup::AddBuffer(const buffer_clone_info& info, BBuffer** _buffer)
148{
149	CALLED();
150	if (fInitError != B_OK)
151		return B_NO_INIT;
152
153	status_t status = fBufferList->AddBuffer(fReclaimSem, info, _buffer);
154	if (status != B_OK) {
155		ERROR("BBufferGroup: error when adding buffer\n");
156		return status;
157	}
158	atomic_add(&fBufferCount, 1);
159	return B_OK;
160}
161
162
163BBuffer*
164BBufferGroup::RequestBuffer(size_t size, bigtime_t timeout)
165{
166	CALLED();
167	if (fInitError != B_OK)
168		return NULL;
169
170	if (size <= 0)
171		return NULL;
172
173	BBuffer *buffer = NULL;
174	fRequestError = fBufferList->RequestBuffer(fReclaimSem, fBufferCount,
175		size, 0, &buffer, timeout);
176
177	return fRequestError == B_OK ? buffer : NULL;
178}
179
180
181status_t
182BBufferGroup::RequestBuffer(BBuffer* buffer, bigtime_t timeout)
183{
184	CALLED();
185	if (fInitError != B_OK)
186		return B_NO_INIT;
187
188	if (buffer == NULL)
189		return B_BAD_VALUE;
190
191	fRequestError = fBufferList->RequestBuffer(fReclaimSem, fBufferCount, 0, 0,
192		&buffer, timeout);
193
194	return fRequestError;
195}
196
197
198status_t
199BBufferGroup::RequestError()
200{
201	CALLED();
202	if (fInitError != B_OK)
203		return B_NO_INIT;
204
205	return fRequestError;
206}
207
208
209status_t
210BBufferGroup::CountBuffers(int32* _count)
211{
212	CALLED();
213	if (fInitError != B_OK)
214		return B_NO_INIT;
215
216	*_count = fBufferCount;
217	return B_OK;
218}
219
220
221status_t
222BBufferGroup::GetBufferList(int32 bufferCount, BBuffer** _buffers)
223{
224	CALLED();
225	if (fInitError != B_OK)
226		return B_NO_INIT;
227
228	if (bufferCount <= 0 || bufferCount > fBufferCount)
229		return B_BAD_VALUE;
230
231	return fBufferList->GetBufferList(fReclaimSem, bufferCount, _buffers);
232}
233
234
235status_t
236BBufferGroup::WaitForBuffers()
237{
238	CALLED();
239	if (fInitError != B_OK)
240		return B_NO_INIT;
241
242	// TODO: this function is not really useful anyway, and will
243	// not work exactly as documented, but it is close enough
244
245	if (fBufferCount < 0)
246		return B_BAD_VALUE;
247	if (fBufferCount == 0)
248		return B_OK;
249
250	// We need to wait until at least one buffer belonging to this group is
251	// reclaimed.
252	// This has happened when can aquire "fReclaimSem"
253
254	status_t status;
255	while ((status = acquire_sem(fReclaimSem)) == B_INTERRUPTED)
256		;
257	if (status != B_OK)
258		return status;
259
260	// we need to release the "fReclaimSem" now, else we would block
261	// requesting of new buffers
262
263	return release_sem(fReclaimSem);
264}
265
266
267status_t
268BBufferGroup::ReclaimAllBuffers()
269{
270	CALLED();
271	if (fInitError != B_OK)
272		return B_NO_INIT;
273
274	// because additional BBuffers might get added to this group betweeen
275	// acquire and release
276	int32 count = fBufferCount;
277
278	if (count < 0)
279		return B_BAD_VALUE;
280	if (count == 0)
281		return B_OK;
282
283	// we need to wait until all BBuffers belonging to this group are reclaimed.
284	// this has happened when the "fReclaimSem" can be aquired "fBufferCount"
285	// times
286
287	status_t status = B_ERROR;
288	do {
289		status = acquire_sem_etc(fReclaimSem, count, B_RELATIVE_TIMEOUT, 0);
290	} while (status == B_INTERRUPTED);
291
292	if (status != B_OK)
293		return status;
294
295	// we need to release the "fReclaimSem" now, else we would block
296	// requesting of new buffers
297
298	return release_sem_etc(fReclaimSem, count, 0);
299}
300
301
302//	#pragma mark - deprecated BeOS R4 API
303
304
305status_t
306BBufferGroup::AddBuffersTo(BMessage* message, const char* name, bool needLock)
307{
308	CALLED();
309	if (fInitError != B_OK)
310		return B_NO_INIT;
311
312	// BeOS R4 legacy API. Implemented as a wrapper around GetBufferList
313	// "needLock" is ignored, GetBufferList will do locking
314
315	if (message == NULL)
316		return B_BAD_VALUE;
317
318	if (name == NULL || strlen(name) == 0)
319		return B_BAD_VALUE;
320
321	BBuffer* buffers[fBufferCount];
322	status_t status = GetBufferList(fBufferCount, buffers);
323	if (status != B_OK)
324		return status;
325
326	for (int32 i = 0; i < fBufferCount; i++) {
327		status = message->AddInt32(name, int32(buffers[i]->ID()));
328		if (status != B_OK)
329			return status;
330	}
331
332	return B_OK;
333}
334
335
336//	#pragma mark - private methods
337
338
339/* not implemented */
340//BBufferGroup::BBufferGroup(const BBufferGroup &)
341//BBufferGroup & BBufferGroup::operator=(const BBufferGroup &)
342
343
344status_t
345BBufferGroup::_Init()
346{
347	CALLED();
348
349	// some defaults in case we drop out early
350	fBufferList = NULL;
351	fRequestError = B_ERROR;
352	fBufferCount = 0;
353
354	// Create the reclaim semaphore
355	// This is also used as a system wide unique identifier for this group
356	fReclaimSem = create_sem(0, "buffer reclaim sem");
357	if (fReclaimSem < 0) {
358		ERROR("BBufferGroup::InitBufferGroup: couldn't create fReclaimSem\n");
359		return (status_t)fReclaimSem;
360	}
361
362	fBufferList = BPrivate::SharedBufferList::Get();
363	if (fBufferList == NULL) {
364		ERROR("BBufferGroup::InitBufferGroup: SharedBufferList::Get() "
365			"failed\n");
366		return B_ERROR;
367	}
368
369	return B_OK;
370}
371
372