1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28#include "mmal.h"
29#include "mmal_pool.h"
30#include "core/mmal_buffer_private.h"
31#include "mmal_logging.h"
32
33/** Definition of a pool */
34typedef struct MMAL_POOL_PRIVATE_T
35{
36 MMAL_POOL_T pool; /**< Actual pool */
37
38 MMAL_POOL_BH_CB_T cb; /**< Buffer header release callback */
39 void *userdata; /**< User provided data to pass with callback */
40
41 mmal_pool_allocator_alloc_t allocator_alloc; /**< Allocator for the payload buffers */
42 mmal_pool_allocator_free_t allocator_free; /**< Allocator for the payload buffers */
43 void *allocator_context; /**< Context for the allocator */
44
45 unsigned int header_size; /**< Size of an initialised buffer header structure */
46 unsigned int payload_size;
47
48 unsigned int headers_alloc_num; /**< Number of buffer headers allocated as part of the private structure */
49
50} MMAL_POOL_PRIVATE_T;
51
52#define ROUND_UP(s,align) ((((unsigned long)(s)) & ~((align)-1)) + (align))
53#define ALIGN 8
54
55static void mmal_pool_buffer_header_release(MMAL_BUFFER_HEADER_T *header);
56
57static void *mmal_pool_allocator_default_alloc(void *context, uint32_t size)
58{
59 MMAL_PARAM_UNUSED(context);
60 return vcos_malloc(size, "mmal_pool payload");
61}
62
63static void mmal_pool_allocator_default_free(void *context, void *mem)
64{
65 MMAL_PARAM_UNUSED(context);
66 vcos_free(mem);
67}
68
69static MMAL_STATUS_T mmal_pool_initialise_buffer_headers(MMAL_POOL_T *pool, unsigned int headers,
70 MMAL_BOOL_T reinitialise)
71{
72 MMAL_POOL_PRIVATE_T *private = (MMAL_POOL_PRIVATE_T *)pool;
73 MMAL_BUFFER_HEADER_T *header;
74 uint8_t *payload = NULL;
75 unsigned int i;
76
77 header = (MMAL_BUFFER_HEADER_T *)((uint8_t *)pool->header + ROUND_UP(sizeof(void *)*headers,ALIGN));
78
79 for (i = 0; i < headers; i++)
80 {
81 if (reinitialise)
82 header = mmal_buffer_header_initialise(header, private->header_size);
83
84 if (private->payload_size && private->allocator_alloc)
85 {
86 LOG_TRACE("allocating %u bytes for payload %u/%u", private->payload_size, i, headers);
87 payload = (uint8_t*)private->allocator_alloc(private->allocator_context, private->payload_size);
88 if (! payload)
89 {
90 LOG_ERROR("failed to allocate payload %u/%u", i, headers);
91 return MMAL_ENOMEM;
92 }
93 }
94 else
95 {
96 if (header->priv->pf_payload_free && header->priv->payload && header->priv->payload_size)
97 {
98 LOG_TRACE("freeing %u bytes for payload %u/%u", header->priv->payload_size, i, headers);
99 header->priv->pf_payload_free(header->priv->payload_context, header->priv->payload);
100 }
101 }
102 header->data = payload;
103 header->alloc_size = private->payload_size;
104 header->priv->pf_release = mmal_pool_buffer_header_release;
105 header->priv->owner = (void *)pool;
106 header->priv->refcount = 1;
107 header->priv->payload = payload;
108 header->priv->payload_context = private->allocator_context;
109 header->priv->pf_payload_free = private->allocator_free;
110 header->priv->payload_size = private->payload_size;
111 pool->header[i] = header;
112 pool->headers_num = i+1;
113 header = (MMAL_BUFFER_HEADER_T *)((uint8_t*)header + private->header_size);
114 }
115
116 return MMAL_SUCCESS;
117}
118
119/** Create a pool of MMAL_BUFFER_HEADER_T */
120MMAL_POOL_T *mmal_pool_create(unsigned int headers, uint32_t payload_size)
121{
122 return mmal_pool_create_with_allocator(headers, payload_size, NULL,
123 mmal_pool_allocator_default_alloc, mmal_pool_allocator_default_free);
124}
125
126/** Create a pool of MMAL_BUFFER_HEADER_T */
127MMAL_POOL_T *mmal_pool_create_with_allocator(unsigned int headers, uint32_t payload_size,
128 void *allocator_context, mmal_pool_allocator_alloc_t allocator_alloc,
129 mmal_pool_allocator_free_t allocator_free)
130{
131 unsigned int i, headers_array_size, header_size, pool_size;
132 MMAL_POOL_PRIVATE_T *private;
133 MMAL_BUFFER_HEADER_T **array;
134 MMAL_POOL_T *pool;
135 MMAL_QUEUE_T *queue;
136
137 queue = mmal_queue_create();
138 if (!queue)
139 {
140 LOG_ERROR("failed to create queue");
141 return NULL;
142 }
143
144 /* Calculate how much memory we need */
145 pool_size = ROUND_UP(sizeof(MMAL_POOL_PRIVATE_T),ALIGN);
146 headers_array_size = ROUND_UP(sizeof(void *)*headers,ALIGN);
147 header_size = ROUND_UP(mmal_buffer_header_size(0),ALIGN);
148
149 LOG_TRACE("allocating %u + %u + %u * %u bytes for pool",
150 pool_size, headers_array_size, header_size, headers);
151 private = vcos_calloc(pool_size, 1, "MMAL pool");
152 array = vcos_calloc(headers_array_size + header_size * headers, 1, "MMAL buffer headers");
153 if (!private || !array)
154 {
155 LOG_ERROR("failed to allocate pool");
156 if (private) vcos_free(private);
157 if (array) vcos_free(array);
158 mmal_queue_destroy(queue);
159 return NULL;
160 }
161 pool = &private->pool;
162 pool->queue = queue;
163 pool->header = (MMAL_BUFFER_HEADER_T **)array;
164 private->header_size = header_size;
165 private->payload_size = payload_size;
166 private->headers_alloc_num = headers;
167
168 /* Use default allocators if none has been specified by client */
169 if (!allocator_alloc || !allocator_free)
170 {
171 allocator_alloc = mmal_pool_allocator_default_alloc;
172 allocator_free = mmal_pool_allocator_default_free;
173 allocator_context = NULL;
174 }
175
176 /* Keep reference to the allocator to allow resizing the payloads at a later point */
177 private->allocator_alloc = allocator_alloc;
178 private->allocator_free = allocator_free;
179 private->allocator_context = allocator_context;
180
181 if (mmal_pool_initialise_buffer_headers(pool, headers, 1) != MMAL_SUCCESS)
182 {
183 mmal_pool_destroy(pool);
184 return NULL;
185 }
186
187 /* Add all the headers to the queue */
188 for (i = 0; i < pool->headers_num; i++)
189 mmal_queue_put(queue, pool->header[i]);
190
191 return pool;
192}
193
194/** Destroy a pool of MMAL_BUFFER_HEADER_T */
195void mmal_pool_destroy(MMAL_POOL_T *pool)
196{
197 unsigned int i;
198
199 if (!pool)
200 return;
201
202 /* If the payload_size is non-zero then the buffer header payload
203 * must be freed. Otherwise it is the caller's responsibility. */
204 for (i = 0; i < pool->headers_num; ++i)
205 {
206 MMAL_BUFFER_HEADER_PRIVATE_T* priv = pool->header[i]->priv;
207
208 if (priv->pf_payload_free && priv->payload && priv->payload_size)
209 priv->pf_payload_free(priv->payload_context, priv->payload);
210 }
211
212 if (pool->header)
213 vcos_free(pool->header);
214
215 if(pool->queue) mmal_queue_destroy(pool->queue);
216 vcos_free(pool);
217}
218
219/** Resize a pool of MMAL_BUFFER_HEADER_T */
220MMAL_STATUS_T mmal_pool_resize(MMAL_POOL_T *pool, unsigned int headers, uint32_t payload_size)
221{
222 MMAL_POOL_PRIVATE_T *private = (MMAL_POOL_PRIVATE_T *)pool;
223 unsigned int i;
224
225 if (!private || !headers)
226 return MMAL_EINVAL;
227
228 /* Check if anything needs to be done */
229 if (headers == pool->headers_num && payload_size == private->payload_size)
230 return MMAL_SUCCESS;
231
232 /* Remove all the headers from the queue */
233 for (i = 0; i < pool->headers_num; i++)
234 mmal_queue_get(pool->queue);
235
236 /* Start by freeing the current payloads */
237 private->payload_size = 0;
238 mmal_pool_initialise_buffer_headers(pool, pool->headers_num, 0);
239 pool->headers_num = 0;
240
241 /* Check if we need to reallocate the buffer headers themselves */
242 if (headers > private->headers_alloc_num)
243 {
244 private->headers_alloc_num = 0;
245 if (pool->header)
246 vcos_free(pool->header);
247 pool->header =
248 vcos_calloc(private->header_size * headers + ROUND_UP(sizeof(void *)*headers,ALIGN),
249 1, "MMAL buffer headers");
250 if (!pool->header)
251 return MMAL_ENOMEM;
252 private->headers_alloc_num = headers;
253 }
254
255 /* Allocate the new payloads */
256 private->payload_size = payload_size;
257 mmal_pool_initialise_buffer_headers(pool, headers, 1);
258
259 /* Add all the headers to the queue */
260 for (i = 0; i < pool->headers_num; i++)
261 mmal_queue_put(pool->queue, pool->header[i]);
262
263 return MMAL_SUCCESS;
264}
265
266/** Buffer header release callback.
267 * Call out to a further client callback and put the buffer back in the queue
268 * so it can be reused, unless the client callback prevents it. */
269static void mmal_pool_buffer_header_release(MMAL_BUFFER_HEADER_T *header)
270{
271 MMAL_POOL_T *pool = (MMAL_POOL_T *)header->priv->owner;
272 MMAL_POOL_PRIVATE_T *private = (MMAL_POOL_PRIVATE_T *)pool;
273 MMAL_BOOL_T queue_buffer = 1;
274
275 header->priv->refcount = 1;
276 if(private->cb)
277 queue_buffer = private->cb(pool, header, private->userdata);
278 if (queue_buffer)
279 mmal_queue_put(pool->queue, header);
280}
281
282/** Set a buffer header release callback to the pool */
283void mmal_pool_callback_set(MMAL_POOL_T *pool, MMAL_POOL_BH_CB_T cb, void *userdata)
284{
285 MMAL_POOL_PRIVATE_T *private = (MMAL_POOL_PRIVATE_T *)pool;
286 private->cb = cb;
287 private->userdata = userdata;
288}
289
290/* Set a pre-release callback for all buffer headers in the pool */
291void mmal_pool_pre_release_callback_set(MMAL_POOL_T *pool, MMAL_BH_PRE_RELEASE_CB_T cb, void *userdata)
292{
293 unsigned int i;
294 MMAL_POOL_PRIVATE_T *private = (MMAL_POOL_PRIVATE_T *)pool;
295 MMAL_BUFFER_HEADER_T *header =
296 (MMAL_BUFFER_HEADER_T*)((uint8_t*)pool->header + ROUND_UP(sizeof(void*)*pool->headers_num,ALIGN));
297
298 for (i = 0; i < pool->headers_num; ++i)
299 {
300 mmal_buffer_header_pre_release_cb_set(header, cb, userdata);
301 header = (MMAL_BUFFER_HEADER_T *)((uint8_t*)header + private->header_size);
302 }
303}
304