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 "util/mmal_util.h"
30#include "util/mmal_component_wrapper.h"
31#include "mmal_logging.h"
32#include <stdio.h>
33
34typedef struct
35{
36 MMAL_WRAPPER_T wrapper; /**< Must be the first member! */
37
38 VCOS_SEMAPHORE_T sema;
39
40} MMAL_WRAPPER_PRIVATE_T;
41
42/** Callback from a control port. Error events will be received there. */
43static void mmal_wrapper_control_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
44{
45 MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)port->userdata;
46 LOG_TRACE("%s(%p),%p,%4.4s", port->name, port, buffer, (char *)&buffer->cmd);
47
48 if (buffer->cmd == MMAL_EVENT_ERROR)
49 {
50 private->wrapper.status = *(MMAL_STATUS_T *)buffer->data;
51 mmal_buffer_header_release(buffer);
52
53 vcos_semaphore_post(&private->sema);
54
55 if (private->wrapper.callback)
56 private->wrapper.callback(&private->wrapper);
57 return;
58 }
59
60 mmal_buffer_header_release(buffer);
61}
62
63/** Callback from an input port. Buffer is released. */
64static void mmal_wrapper_bh_in_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
65{
66 MMAL_PARAM_UNUSED(port);
67 LOG_TRACE("(%s)%p,%p,%p,%i", port->name, port, buffer, buffer->data, (int)buffer->length);
68
69 /* We're done with the buffer, just recycle it */
70 mmal_buffer_header_release(buffer);
71}
72
73/** Callback from an output port. Buffer is queued for the next component. */
74static void mmal_wrapper_bh_out_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
75{
76 MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)port->userdata;
77 LOG_TRACE("(%s)%p,%p,%p,%i", port->name, port, buffer, buffer->data, (int)buffer->length);
78
79 /* Queue the buffer produced by the output port */
80 mmal_queue_put(private->wrapper.output_queue[port->index], buffer);
81 vcos_semaphore_post(&private->sema);
82
83 if (private->wrapper.callback)
84 private->wrapper.callback(&private->wrapper);
85}
86
87/** Callback from the pool. Buffer is available. */
88static MMAL_BOOL_T mmal_wrapper_bh_release_cb(MMAL_POOL_T *pool, MMAL_BUFFER_HEADER_T *buffer,
89 void *userdata)
90{
91 MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)userdata;
92
93 mmal_queue_put(pool->queue, buffer);
94 vcos_semaphore_post(&private->sema);
95
96 if (private->wrapper.callback)
97 private->wrapper.callback(&private->wrapper);
98
99 return 0;
100}
101
102/*****************************************************************************/
103MMAL_STATUS_T mmal_wrapper_destroy(MMAL_WRAPPER_T *wrapper)
104{
105 MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)wrapper;
106 unsigned int i;
107
108 LOG_TRACE("%p, %s", wrapper, wrapper->component->name);
109
110 /* Cleanup resources */
111 mmal_component_destroy(wrapper->component);
112
113 for (i = 0; i < wrapper->input_num; i++)
114 {
115 if (wrapper->input_pool[i])
116 mmal_pool_destroy(wrapper->input_pool[i]);
117 }
118
119 for (i = 0; i < wrapper->output_num; i++)
120 {
121 if (wrapper->output_pool[i])
122 mmal_pool_destroy(wrapper->output_pool[i]);
123 if (wrapper->output_queue[i])
124 mmal_queue_destroy(wrapper->output_queue[i]);
125 }
126
127 vcos_semaphore_delete(&private->sema);
128 vcos_free(private);
129 return MMAL_SUCCESS;
130}
131
132/*****************************************************************************/
133MMAL_STATUS_T mmal_wrapper_create(MMAL_WRAPPER_T **ctx, const char *name)
134{
135 MMAL_STATUS_T status;
136 MMAL_COMPONENT_T *component;
137 MMAL_WRAPPER_PRIVATE_T *private;
138 MMAL_WRAPPER_T *wrapper;
139 int64_t start_time;
140 unsigned int i, extra_size;
141
142 LOG_TRACE("wrapper %p, name %s", ctx, name);
143
144 /* Sanity checking */
145 if (!ctx || !name)
146 return MMAL_EINVAL;
147
148 start_time = vcos_getmicrosecs();
149
150 status = mmal_component_create(name, &component);
151 if (status != MMAL_SUCCESS)
152 return status;
153
154 extra_size = (component->input_num * sizeof(MMAL_POOL_T*)) + (component->output_num * (sizeof(MMAL_POOL_T*) + sizeof(MMAL_QUEUE_T*)));
155 private = vcos_calloc(1, sizeof(*private) + extra_size, "mmal wrapper");
156 if (!private)
157 {
158 mmal_component_destroy(component);
159 return MMAL_ENOMEM;
160 }
161
162 if (vcos_semaphore_create(&private->sema, "mmal wrapper", 0) != VCOS_SUCCESS)
163 {
164 mmal_component_destroy(component);
165 vcos_free(private);
166 return MMAL_ENOMEM;
167 }
168
169 wrapper = &private->wrapper;
170 wrapper->component = component;
171 wrapper->control = component->control;
172 wrapper->input_num = component->input_num;
173 wrapper->input = component->input;
174 wrapper->output_num = component->output_num;
175 wrapper->output = component->output;
176 wrapper->input_pool = (MMAL_POOL_T **)&private[1];
177 wrapper->output_pool = (MMAL_POOL_T **)&wrapper->input_pool[component->input_num];
178 wrapper->output_queue = (MMAL_QUEUE_T **)&wrapper->output_pool[component->output_num];
179
180 /* Create our pools and queues */
181 for (i = 0; i < wrapper->input_num; i++)
182 {
183 wrapper->input_pool[i] = mmal_port_pool_create(wrapper->input[i], 0, 0);
184 if (!wrapper->input_pool[i])
185 goto error;
186 mmal_pool_callback_set(wrapper->input_pool[i], mmal_wrapper_bh_release_cb, (void *)wrapper);
187
188 wrapper->input[i]->userdata = (void *)wrapper;
189 }
190 for (i = 0; i < wrapper->output_num; i++)
191 {
192 wrapper->output_pool[i] = mmal_port_pool_create(wrapper->output[i], 0, 0);
193 wrapper->output_queue[i] = mmal_queue_create();
194 if (!wrapper->output_pool[i] || !wrapper->output_queue[i])
195 goto error;
196 mmal_pool_callback_set(wrapper->output_pool[i], mmal_wrapper_bh_release_cb, (void *)wrapper);
197
198 wrapper->output[i]->userdata = (void *)wrapper;
199 }
200
201 /* Setup control port */
202 wrapper->control->userdata = (void *)wrapper;
203 status = mmal_port_enable(wrapper->control, mmal_wrapper_control_cb);
204 if (status != MMAL_SUCCESS)
205 goto error;
206
207 wrapper->time_setup = vcos_getmicrosecs() - start_time;
208 *ctx = wrapper;
209 return MMAL_SUCCESS;
210
211 error:
212 mmal_wrapper_destroy(wrapper);
213 return status == MMAL_SUCCESS ? MMAL_ENOMEM : status;
214}
215
216/*****************************************************************************/
217MMAL_STATUS_T mmal_wrapper_port_enable(MMAL_PORT_T *port, uint32_t flags)
218{
219 MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)port->userdata;
220 MMAL_WRAPPER_T *wrapper = &private->wrapper;
221 int64_t start_time = vcos_getmicrosecs();
222 uint32_t buffer_size;
223 MMAL_STATUS_T status;
224 MMAL_POOL_T *pool;
225
226 LOG_TRACE("%p, %s", wrapper, port->name);
227
228 if (port->type != MMAL_PORT_TYPE_INPUT && port->type != MMAL_PORT_TYPE_OUTPUT)
229 return MMAL_EINVAL;
230
231 if (port->is_enabled)
232 return MMAL_SUCCESS;
233
234 pool = port->type == MMAL_PORT_TYPE_INPUT ?
235 wrapper->input_pool[port->index] : wrapper->output_pool[port->index];
236 buffer_size = (flags & MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE) ? port->buffer_size : 0;
237
238 /* FIXME: we don't support switching between shared and non-shared memory.
239 * We would need to save the flag and force a pool resize when switching. */
240 if (flags & MMAL_WRAPPER_FLAG_PAYLOAD_USE_SHARED_MEMORY)
241 {
242 MMAL_PARAMETER_BOOLEAN_T param_zc =
243 {{MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T)}, 1};
244 status = mmal_port_parameter_set(port, &param_zc.hdr);
245 if (status != MMAL_SUCCESS && status != MMAL_ENOSYS)
246 {
247 LOG_ERROR("failed to set zero copy on %s", port->name);
248 return status;
249 }
250 }
251
252 /* Resize the pool */
253 status = mmal_pool_resize(pool, port->buffer_num, buffer_size);
254 if (status != MMAL_SUCCESS)
255 {
256 LOG_ERROR("could not resize pool (%i/%i)", (int)port->buffer_num, (int)buffer_size);
257 return status;
258 }
259
260 /* Enable port. The callback specified here is the function which
261 * will be called when a buffer header comes back to the port. */
262 status = mmal_port_enable(port, port->type == MMAL_PORT_TYPE_INPUT ?
263 mmal_wrapper_bh_in_cb : mmal_wrapper_bh_out_cb);
264 if (status != MMAL_SUCCESS)
265 {
266 LOG_ERROR("could not enable port");
267 return status;
268 }
269
270 wrapper->time_enable += vcos_getmicrosecs() - start_time;
271 return MMAL_SUCCESS;
272}
273
274/*****************************************************************************/
275MMAL_STATUS_T mmal_wrapper_port_disable(MMAL_PORT_T *port)
276{
277 MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)port->userdata;
278 MMAL_WRAPPER_T *wrapper = &private->wrapper;
279 int64_t start_time = vcos_getmicrosecs();
280 MMAL_STATUS_T status;
281
282 LOG_TRACE("%p, %s", wrapper, port->name);
283
284 if (port->type != MMAL_PORT_TYPE_INPUT && port->type != MMAL_PORT_TYPE_OUTPUT)
285 return MMAL_EINVAL;
286
287 if (!port->is_enabled)
288 return MMAL_SUCCESS;
289
290 /* Disable port */
291 status = mmal_port_disable(port);
292 if (status != MMAL_SUCCESS)
293 {
294 LOG_ERROR("could not disable port");
295 return status;
296 }
297
298 /* Flush the queue */
299 if (port->type == MMAL_PORT_TYPE_OUTPUT)
300 {
301 MMAL_POOL_T *pool = wrapper->output_pool[port->index];
302 MMAL_QUEUE_T *queue = wrapper->output_queue[port->index];
303 MMAL_BUFFER_HEADER_T *buffer;
304
305 while ((buffer = mmal_queue_get(queue)) != NULL)
306 mmal_buffer_header_release(buffer);
307
308 if ( !vcos_verify(mmal_queue_length(pool->queue) == pool->headers_num) )
309 {
310 LOG_ERROR("coul dnot release all buffers");
311 }
312 }
313
314 wrapper->time_disable = vcos_getmicrosecs() - start_time;
315 return status;
316}
317
318/** Wait for an empty buffer to be available on a port */
319MMAL_STATUS_T mmal_wrapper_buffer_get_empty(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T **buffer,
320 uint32_t flags)
321{
322 MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)port->userdata;
323 MMAL_WRAPPER_T *wrapper = &private->wrapper;
324 MMAL_POOL_T *pool;
325
326 LOG_TRACE("%p, %s", wrapper, port->name);
327
328 if (!buffer || (port->type != MMAL_PORT_TYPE_INPUT && port->type != MMAL_PORT_TYPE_OUTPUT))
329 return MMAL_EINVAL;
330
331 pool = port->type == MMAL_PORT_TYPE_INPUT ?
332 wrapper->input_pool[port->index] : wrapper->output_pool[port->index];
333
334 while (wrapper->status == MMAL_SUCCESS &&
335 (*buffer = mmal_queue_get(pool->queue)) == NULL)
336 {
337 if (!(flags & MMAL_WRAPPER_FLAG_WAIT))
338 break;
339 vcos_semaphore_wait(&private->sema);
340 }
341
342 return wrapper->status == MMAL_SUCCESS && !*buffer ? MMAL_EAGAIN : wrapper->status;
343}
344
345/** Wait for a full buffer to be available on a port */
346MMAL_STATUS_T mmal_wrapper_buffer_get_full(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T **buffer,
347 uint32_t flags)
348{
349 MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)port->userdata;
350 MMAL_WRAPPER_T *wrapper = &private->wrapper;
351 MMAL_QUEUE_T *queue;
352
353 LOG_TRACE("%p, %s", wrapper, port->name);
354
355 if (!buffer || port->type != MMAL_PORT_TYPE_OUTPUT)
356 return MMAL_EINVAL;
357 queue = wrapper->output_queue[port->index];
358
359 while (wrapper->status == MMAL_SUCCESS &&
360 (*buffer = mmal_queue_get(queue)) == NULL)
361 {
362 if (!(flags & MMAL_WRAPPER_FLAG_WAIT))
363 break;
364 vcos_semaphore_wait(&private->sema);
365 }
366
367 return wrapper->status == MMAL_SUCCESS && !*buffer ? MMAL_EAGAIN : wrapper->status;
368}
369