1 | /* |
2 | Copyright (c) 2012, Broadcom Europe Ltd |
3 | All rights reserved. |
4 | |
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, 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 | |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
23 | ON 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 |
25 | SOFTWARE, 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 | |
34 | typedef 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. */ |
43 | static 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. */ |
64 | static 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. */ |
74 | static 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. */ |
88 | static 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 | /*****************************************************************************/ |
103 | MMAL_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 | /*****************************************************************************/ |
133 | MMAL_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, ; |
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 | /*****************************************************************************/ |
217 | MMAL_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, ¶m_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 | /*****************************************************************************/ |
275 | MMAL_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 */ |
319 | MMAL_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 */ |
346 | MMAL_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 | |