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 "core/mmal_component_private.h" |
31 | #include "core/mmal_port_private.h" |
32 | #include "interface/vcos/vcos.h" |
33 | #include "mmal_logging.h" |
34 | #include "interface/mmal/util/mmal_util.h" |
35 | #include "interface/mmal/mmal_parameters.h" |
36 | #include <stdio.h> |
37 | |
38 | #ifdef _VIDEOCORE |
39 | #include "vcfw/rtos/common/rtos_common_mem.h" /* mem_alloc */ |
40 | #endif |
41 | |
42 | /** Only collect port stats if enabled in build. Performance could be |
43 | * affected on an ARM since gettimeofday() involves a system call. |
44 | */ |
45 | #if defined(MMAL_COLLECT_PORT_STATS) |
46 | # define MMAL_COLLECT_PORT_STATS_ENABLED 1 |
47 | #else |
48 | # define MMAL_COLLECT_PORT_STATS_ENABLED 0 |
49 | #endif |
50 | |
51 | static MMAL_STATUS_T mmal_port_private_parameter_get(MMAL_PORT_T *port, |
52 | MMAL_PARAMETER_HEADER_T *param); |
53 | |
54 | static MMAL_STATUS_T mmal_port_private_parameter_set(MMAL_PORT_T *port, |
55 | const MMAL_PARAMETER_HEADER_T *param); |
56 | |
57 | /* Define this if you want to log all buffer transfers */ |
58 | //#define ENABLE_MMAL_EXTRA_LOGGING |
59 | |
60 | /** Definition of the core's private structure for a port. */ |
61 | typedef struct MMAL_PORT_PRIVATE_CORE_T |
62 | { |
63 | VCOS_MUTEX_T lock; /**< Used to lock access to the port */ |
64 | VCOS_MUTEX_T send_lock; /**< Used to lock access while sending buffer to the port */ |
65 | VCOS_MUTEX_T stats_lock; /**< Used to lock access to the stats */ |
66 | VCOS_MUTEX_T connection_lock; /**< Used to lock access to a connection */ |
67 | |
68 | /** Callback set by client to call when buffer headers need to be returned */ |
69 | MMAL_PORT_BH_CB_T ; |
70 | |
71 | /** Keeps track of the number of buffer headers currently in transit in this port */ |
72 | int32_t ; |
73 | VCOS_MUTEX_T transit_lock; |
74 | VCOS_SEMAPHORE_T transit_sema; |
75 | |
76 | /** Copy of the public port format pointer, to detect accidental overwrites */ |
77 | MMAL_ES_FORMAT_T* format_ptr_copy; |
78 | |
79 | /** Port to which this port is connected, or NULL if disconnected */ |
80 | MMAL_PORT_T* connected_port; |
81 | |
82 | MMAL_BOOL_T core_owns_connection; /**< Connection is handled by the core */ |
83 | |
84 | /** Pool of buffers used between connected ports - output port only */ |
85 | MMAL_POOL_T* pool_for_connection; |
86 | |
87 | /** Indicates whether the port is paused or not. Buffers received on |
88 | * a paused port will be queued instead of being sent to the component. */ |
89 | MMAL_BOOL_T is_paused; |
90 | /** Queue for buffers received from the client when in paused state */ |
91 | MMAL_BUFFER_HEADER_T* queue_first; |
92 | /** Queue for buffers received from the client when in paused state */ |
93 | MMAL_BUFFER_HEADER_T** queue_last; |
94 | |
95 | /** Per-port statistics collected directly by the MMAL core */ |
96 | MMAL_CORE_PORT_STATISTICS_T stats; |
97 | |
98 | char *name; /**< Port name */ |
99 | unsigned int name_size; /** Size of the memory area reserved for the name string */ |
100 | } MMAL_PORT_PRIVATE_CORE_T; |
101 | |
102 | /***************************************************************************** |
103 | * Static declarations |
104 | *****************************************************************************/ |
105 | static MMAL_STATUS_T mmal_port_enable_internal(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb); |
106 | static MMAL_STATUS_T mmal_port_disable_internal(MMAL_PORT_T *port); |
107 | |
108 | static MMAL_STATUS_T mmal_port_connection_enable(MMAL_PORT_T *port, MMAL_PORT_T *connected_port); |
109 | static MMAL_STATUS_T mmal_port_connection_disable(MMAL_PORT_T *port, MMAL_PORT_T *connected_port); |
110 | static MMAL_STATUS_T mmal_port_connection_start(MMAL_PORT_T *port, MMAL_PORT_T *connected_port); |
111 | static MMAL_STATUS_T mmal_port_populate_from_pool(MMAL_PORT_T* port, MMAL_POOL_T* pool); |
112 | static MMAL_STATUS_T mmal_port_populate_clock_ports(MMAL_PORT_T* output, MMAL_PORT_T* input, MMAL_POOL_T* pool); |
113 | static MMAL_STATUS_T mmal_port_connect_default(MMAL_PORT_T *port, MMAL_PORT_T *other_port); |
114 | static void mmal_port_connected_input_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); |
115 | static void mmal_port_connected_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); |
116 | static MMAL_BOOL_T mmal_port_connected_pool_cb(MMAL_POOL_T *pool, MMAL_BUFFER_HEADER_T *buffer, void *userdata); |
117 | |
118 | static void mmal_port_name_update(MMAL_PORT_T *port); |
119 | static void mmal_port_update_port_stats(MMAL_PORT_T *port, MMAL_CORE_STATS_DIR direction); |
120 | |
121 | /*****************************************************************************/ |
122 | |
123 | /* Macros used to make the port API thread safe */ |
124 | #define LOCK_PORT(a) vcos_mutex_lock(&(a)->priv->core->lock); |
125 | #define UNLOCK_PORT(a) vcos_mutex_unlock(&(a)->priv->core->lock); |
126 | |
127 | /* Macros used to make the buffer sending / flushing thread safe */ |
128 | #define LOCK_SENDING(a) vcos_mutex_lock(&(a)->priv->core->send_lock); |
129 | #define UNLOCK_SENDING(a) vcos_mutex_unlock(&(a)->priv->core->send_lock); |
130 | |
131 | /* Macros used to make the port connection API thread safe */ |
132 | #define LOCK_CONNECTION(a) vcos_mutex_lock(&(a)->priv->core->connection_lock); |
133 | #define UNLOCK_CONNECTION(a) vcos_mutex_unlock(&(a)->priv->core->connection_lock); |
134 | |
135 | /* Macros used to make mmal_port_disable() blocking until all |
136 | * the buffers have been sent back to the client */ |
137 | #define IN_TRANSIT_INCREMENT(a) \ |
138 | vcos_mutex_lock(&(a)->priv->core->transit_lock); \ |
139 | if (!(a)->priv->core->transit_buffer_headers++) \ |
140 | vcos_semaphore_wait(&(a)->priv->core->transit_sema); \ |
141 | vcos_mutex_unlock(&(a)->priv->core->transit_lock) |
142 | #define IN_TRANSIT_DECREMENT(a) \ |
143 | vcos_mutex_lock(&(a)->priv->core->transit_lock); \ |
144 | if (!--(a)->priv->core->transit_buffer_headers) \ |
145 | vcos_semaphore_post(&(a)->priv->core->transit_sema); \ |
146 | vcos_mutex_unlock(&(a)->priv->core->transit_lock) |
147 | #define IN_TRANSIT_WAIT(a) \ |
148 | vcos_semaphore_wait(&(a)->priv->core->transit_sema); \ |
149 | vcos_semaphore_post(&(a)->priv->core->transit_sema) |
150 | #define IN_TRANSIT_COUNT(a) \ |
151 | (a)->priv->core->transit_buffer_headers |
152 | |
153 | #define PORT_NAME_FORMAT "%s:%.2222s:%i%c%4.4s)" |
154 | |
155 | /*****************************************************************************/ |
156 | |
157 | /** Allocate a port structure */ |
158 | MMAL_PORT_T *mmal_port_alloc(MMAL_COMPONENT_T *component, MMAL_PORT_TYPE_T type, unsigned int ) |
159 | { |
160 | MMAL_PORT_T *port; |
161 | MMAL_PORT_PRIVATE_CORE_T *core; |
162 | unsigned int name_size = strlen(component->name) + sizeof(PORT_NAME_FORMAT); |
163 | unsigned int size = sizeof(*port) + sizeof(MMAL_PORT_PRIVATE_T) + |
164 | sizeof(MMAL_PORT_PRIVATE_CORE_T) + name_size + extra_size; |
165 | MMAL_BOOL_T lock = 0, lock_send = 0, lock_transit = 0, sema_transit = 0; |
166 | MMAL_BOOL_T lock_stats = 0, lock_connection = 0; |
167 | |
168 | LOG_TRACE("component:%s type:%u extra:%u" , component->name, type, extra_size); |
169 | |
170 | port = vcos_calloc(1, size, "mmal port" ); |
171 | if (!port) |
172 | { |
173 | LOG_ERROR("failed to allocate port, size %u" , size); |
174 | return 0; |
175 | } |
176 | port->type = type; |
177 | |
178 | port->priv = (MMAL_PORT_PRIVATE_T *)(port+1); |
179 | port->priv->core = core = (MMAL_PORT_PRIVATE_CORE_T *)(port->priv+1); |
180 | if (extra_size) |
181 | port->priv->module = (struct MMAL_PORT_MODULE_T *)(port->priv->core+1); |
182 | port->component = component; |
183 | port->name = core->name = ((char *)(port->priv->core+1)) + extra_size; |
184 | core->name_size = name_size; |
185 | mmal_port_name_update(port); |
186 | core->queue_last = &core->queue_first; |
187 | |
188 | port->priv->pf_connect = mmal_port_connect_default; |
189 | |
190 | lock = vcos_mutex_create(&port->priv->core->lock, "mmal port lock" ) == VCOS_SUCCESS; |
191 | lock_send = vcos_mutex_create(&port->priv->core->send_lock, "mmal port send lock" ) == VCOS_SUCCESS; |
192 | lock_transit = vcos_mutex_create(&port->priv->core->transit_lock, "mmal port transit lock" ) == VCOS_SUCCESS; |
193 | sema_transit = vcos_semaphore_create(&port->priv->core->transit_sema, "mmal port transit sema" , 1) == VCOS_SUCCESS; |
194 | lock_stats = vcos_mutex_create(&port->priv->core->stats_lock, "mmal stats lock" ) == VCOS_SUCCESS; |
195 | lock_connection = vcos_mutex_create(&port->priv->core->connection_lock, "mmal connection lock" ) == VCOS_SUCCESS; |
196 | |
197 | if (!lock || !lock_send || !lock_transit || !sema_transit || !lock_stats || !lock_connection) |
198 | { |
199 | LOG_ERROR("%s: failed to create sync objects (%u,%u,%u,%u,%u,%u)" , |
200 | port->name, lock, lock_send, lock_transit, sema_transit, lock_stats, lock_connection); |
201 | goto error; |
202 | } |
203 | |
204 | port->format = mmal_format_alloc(); |
205 | if (!port->format) |
206 | { |
207 | LOG_ERROR("%s: failed to allocate format object" , port->name); |
208 | goto error; |
209 | } |
210 | port->priv->core->format_ptr_copy = port->format; |
211 | |
212 | LOG_TRACE("%s: created at %p" , port->name, port); |
213 | return port; |
214 | |
215 | error: |
216 | if (lock) vcos_mutex_delete(&port->priv->core->lock); |
217 | if (lock_send) vcos_mutex_delete(&port->priv->core->send_lock); |
218 | if (lock_transit) vcos_mutex_delete(&port->priv->core->transit_lock); |
219 | if (sema_transit) vcos_semaphore_delete(&port->priv->core->transit_sema); |
220 | if (lock_stats) vcos_mutex_delete(&port->priv->core->stats_lock); |
221 | if (lock_connection) vcos_mutex_delete(&port->priv->core->connection_lock); |
222 | if (port->format) mmal_format_free(port->format); |
223 | vcos_free(port); |
224 | return 0; |
225 | } |
226 | |
227 | /** Free a port structure */ |
228 | void mmal_port_free(MMAL_PORT_T *port) |
229 | { |
230 | LOG_TRACE("%s at %p" , port ? port->name : "<invalid>" , port); |
231 | |
232 | if (!port) |
233 | return; |
234 | |
235 | vcos_assert(port->format == port->priv->core->format_ptr_copy); |
236 | mmal_format_free(port->priv->core->format_ptr_copy); |
237 | vcos_mutex_delete(&port->priv->core->connection_lock); |
238 | vcos_mutex_delete(&port->priv->core->stats_lock); |
239 | vcos_semaphore_delete(&port->priv->core->transit_sema); |
240 | vcos_mutex_delete(&port->priv->core->transit_lock); |
241 | vcos_mutex_delete(&port->priv->core->send_lock); |
242 | vcos_mutex_delete(&port->priv->core->lock); |
243 | vcos_free(port); |
244 | } |
245 | |
246 | /** Allocate an array of ports */ |
247 | MMAL_PORT_T **mmal_ports_alloc(MMAL_COMPONENT_T *component, unsigned int ports_num, |
248 | MMAL_PORT_TYPE_T type, unsigned int ) |
249 | { |
250 | MMAL_PORT_T **ports; |
251 | unsigned int i; |
252 | |
253 | ports = vcos_calloc(1, sizeof(MMAL_PORT_T *) * ports_num, "mmal ports" ); |
254 | if (!ports) |
255 | return 0; |
256 | |
257 | for (i = 0; i < ports_num; i++) |
258 | { |
259 | ports[i] = mmal_port_alloc(component, type, extra_size); |
260 | if (!ports[i]) |
261 | break; |
262 | ports[i]->index = i; |
263 | mmal_port_name_update(ports[i]); |
264 | } |
265 | |
266 | if (i != ports_num) |
267 | { |
268 | for (ports_num = i, i = 0; i < ports_num; i++) |
269 | mmal_port_free(ports[i]); |
270 | vcos_free(ports); |
271 | return 0; |
272 | } |
273 | |
274 | return ports; |
275 | } |
276 | |
277 | /** Free an array of ports */ |
278 | void mmal_ports_free(MMAL_PORT_T **ports, unsigned int ports_num) |
279 | { |
280 | unsigned int i; |
281 | |
282 | for (i = 0; i < ports_num; i++) |
283 | mmal_port_free(ports[i]); |
284 | vcos_free(ports); |
285 | } |
286 | |
287 | /** Set format of a port */ |
288 | MMAL_STATUS_T mmal_port_format_commit(MMAL_PORT_T *port) |
289 | { |
290 | MMAL_STATUS_T status; |
291 | char encoding_string[16]; |
292 | |
293 | if (!port || !port->priv) |
294 | { |
295 | LOG_ERROR("invalid port (%p/%p)" , port, port ? port->priv : NULL); |
296 | return MMAL_EINVAL; |
297 | } |
298 | |
299 | if (port->format != port->priv->core->format_ptr_copy) |
300 | { |
301 | LOG_ERROR("%s: port format has been overwritten, resetting %p to %p" , |
302 | port->name, port->format, port->priv->core->format_ptr_copy); |
303 | port->format = port->priv->core->format_ptr_copy; |
304 | return MMAL_EFAULT; |
305 | } |
306 | |
307 | if (port->format->encoding == 0) |
308 | snprintf(encoding_string, sizeof(encoding_string), "<NO-FORMAT>" ); |
309 | else |
310 | snprintf(encoding_string, sizeof(encoding_string), "%4.4s" , (char*)&port->format->encoding); |
311 | |
312 | LOG_TRACE("%s(%i:%i) port %p format %i:%s" , |
313 | port->component->name, (int)port->type, (int)port->index, port, |
314 | (int)port->format->type, encoding_string); |
315 | |
316 | if (!port->priv->pf_set_format) |
317 | { |
318 | LOG_ERROR("%s: no component implementation" , port->name); |
319 | return MMAL_ENOSYS; |
320 | } |
321 | |
322 | LOCK_PORT(port); |
323 | status = port->priv->pf_set_format(port); |
324 | mmal_port_name_update(port); |
325 | |
326 | /* Make sure the buffer size / num are sensible */ |
327 | if (port->buffer_size < port->buffer_size_min) |
328 | port->buffer_size = port->buffer_size_min; |
329 | if (port->buffer_num < port->buffer_num_min) |
330 | port->buffer_num = port->buffer_num_min; |
331 | /* The output port settings might have changed */ |
332 | if (port->type == MMAL_PORT_TYPE_INPUT) |
333 | { |
334 | MMAL_PORT_T **ports = port->component->output; |
335 | unsigned int i; |
336 | |
337 | for (i = 0; i < port->component->output_num; i++) |
338 | { |
339 | if (ports[i]->buffer_size < ports[i]->buffer_size_min) |
340 | ports[i]->buffer_size = ports[i]->buffer_size_min; |
341 | if (ports[i]->buffer_num < ports[i]->buffer_num_min) |
342 | ports[i]->buffer_num = ports[i]->buffer_num_min; |
343 | } |
344 | } |
345 | |
346 | UNLOCK_PORT(port); |
347 | return status; |
348 | } |
349 | |
350 | /** Enable processing on a port */ |
351 | MMAL_STATUS_T mmal_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb) |
352 | { |
353 | MMAL_STATUS_T status; |
354 | MMAL_PORT_T *connected_port; |
355 | MMAL_PORT_PRIVATE_CORE_T *core; |
356 | |
357 | if (!port || !port->priv) |
358 | return MMAL_EINVAL; |
359 | |
360 | LOG_TRACE("%s port %p, cb %p, buffers (%i/%i/%i,%i/%i/%i)" , |
361 | port->name, port, cb, |
362 | (int)port->buffer_num, (int)port->buffer_num_recommended, (int)port->buffer_num_min, |
363 | (int)port->buffer_size, (int)port->buffer_size_recommended, (int)port->buffer_size_min); |
364 | |
365 | if (!port->priv->pf_enable) |
366 | return MMAL_ENOSYS; |
367 | |
368 | core = port->priv->core; |
369 | LOCK_CONNECTION(port); |
370 | connected_port = core->connected_port; |
371 | |
372 | /* Sanity checking */ |
373 | if (port->is_enabled) |
374 | { |
375 | UNLOCK_CONNECTION(port); |
376 | LOG_ERROR("%s(%p) already enabled" , port->name, port); |
377 | return MMAL_EINVAL; |
378 | } |
379 | if (connected_port && cb) /* Callback must be NULL for connected ports */ |
380 | { |
381 | UNLOCK_CONNECTION(port); |
382 | LOG_ERROR("callback (%p) not allowed for connected port (%s)%p" , |
383 | cb, port->name, connected_port); |
384 | return MMAL_EINVAL; |
385 | } |
386 | |
387 | /* Start by preparing the port connection so that everything is ready for when |
388 | * both ports are enabled */ |
389 | if (connected_port) |
390 | { |
391 | LOCK_CONNECTION(connected_port); |
392 | status = mmal_port_connection_enable(port, connected_port); |
393 | if (status != MMAL_SUCCESS) |
394 | { |
395 | UNLOCK_CONNECTION(connected_port); |
396 | UNLOCK_CONNECTION(port); |
397 | return status; |
398 | } |
399 | |
400 | cb = connected_port->type == MMAL_PORT_TYPE_INPUT ? |
401 | mmal_port_connected_output_cb : mmal_port_connected_input_cb; |
402 | } |
403 | |
404 | /* Enable the input port of a connection first */ |
405 | if (connected_port && connected_port->type == MMAL_PORT_TYPE_INPUT) |
406 | { |
407 | status = mmal_port_enable_internal(connected_port, mmal_port_connected_input_cb); |
408 | if (status != MMAL_SUCCESS) |
409 | { |
410 | LOG_ERROR("failed to enable connected port (%s)%p (%s)" , connected_port->name, |
411 | connected_port, mmal_status_to_string(status)); |
412 | goto error; |
413 | } |
414 | } |
415 | |
416 | status = mmal_port_enable_internal(port, cb); |
417 | if (status != MMAL_SUCCESS) |
418 | { |
419 | LOG_ERROR("failed to enable port %s(%p) (%s)" , port->name, port, |
420 | mmal_status_to_string(status)); |
421 | goto error; |
422 | } |
423 | |
424 | /* Enable the output port of a connection last */ |
425 | if (connected_port && connected_port->type != MMAL_PORT_TYPE_INPUT) |
426 | { |
427 | status = mmal_port_enable_internal(connected_port, mmal_port_connected_output_cb); |
428 | if (status != MMAL_SUCCESS) |
429 | { |
430 | LOG_ERROR("failed to enable connected port (%s)%p (%s)" , connected_port->name, |
431 | connected_port, mmal_status_to_string(status)); |
432 | goto error; |
433 | } |
434 | } |
435 | |
436 | /* Kick off the connection */ |
437 | if (connected_port && core->core_owns_connection) |
438 | { |
439 | status = mmal_port_connection_start(port, connected_port); |
440 | if (status != MMAL_SUCCESS) |
441 | { |
442 | LOG_ERROR("failed to start connection (%s)%p (%s)" , port->name, |
443 | port, mmal_status_to_string(status)); |
444 | goto error; |
445 | } |
446 | } |
447 | |
448 | if (connected_port) |
449 | UNLOCK_CONNECTION(connected_port); |
450 | UNLOCK_CONNECTION(port); |
451 | return MMAL_SUCCESS; |
452 | |
453 | error: |
454 | if (connected_port && connected_port->is_enabled) |
455 | mmal_port_disable_internal(connected_port); |
456 | if (port->is_enabled) |
457 | mmal_port_disable_internal(port); |
458 | if (connected_port) |
459 | mmal_port_connection_disable(port, connected_port); |
460 | |
461 | if (connected_port) |
462 | UNLOCK_CONNECTION(connected_port); |
463 | UNLOCK_CONNECTION(port); |
464 | return status; |
465 | } |
466 | |
467 | static MMAL_STATUS_T mmal_port_enable_internal(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb) |
468 | { |
469 | MMAL_PORT_PRIVATE_CORE_T* core = port->priv->core; |
470 | MMAL_STATUS_T status = MMAL_SUCCESS; |
471 | |
472 | LOCK_PORT(port); |
473 | |
474 | if (port->is_enabled) |
475 | goto end; |
476 | |
477 | /* Sanity check the buffer requirements */ |
478 | if (port->buffer_num < port->buffer_num_min) |
479 | { |
480 | LOG_ERROR("buffer_num too small (%i/%i)" , (int)port->buffer_num, (int)port->buffer_num_min); |
481 | status = MMAL_EINVAL; |
482 | goto end; |
483 | } |
484 | if (port->buffer_size < port->buffer_size_min) |
485 | { |
486 | LOG_ERROR("buffer_size too small (%i/%i)" , (int)port->buffer_size, (int)port->buffer_size_min); |
487 | status = MMAL_EINVAL; |
488 | goto end; |
489 | } |
490 | |
491 | core->buffer_header_callback = cb; |
492 | status = port->priv->pf_enable(port, cb); |
493 | if (status != MMAL_SUCCESS) |
494 | goto end; |
495 | |
496 | LOCK_SENDING(port); |
497 | port->is_enabled = 1; |
498 | UNLOCK_SENDING(port); |
499 | |
500 | end: |
501 | UNLOCK_PORT(port); |
502 | return status; |
503 | } |
504 | |
505 | static MMAL_STATUS_T mmal_port_connection_enable(MMAL_PORT_T *port, MMAL_PORT_T *connected_port) |
506 | { |
507 | MMAL_PORT_T *output = port->type == MMAL_PORT_TYPE_OUTPUT ? port : connected_port; |
508 | MMAL_PORT_T *input = connected_port->type == MMAL_PORT_TYPE_INPUT ? connected_port : port; |
509 | MMAL_PORT_T *pool_port = (output->capabilities & MMAL_PORT_CAPABILITY_ALLOCATION) ? output : input; |
510 | MMAL_PORT_PRIVATE_CORE_T *pool_core = pool_port->priv->core; |
511 | uint32_t buffer_size, buffer_num; |
512 | MMAL_POOL_T *pool; |
513 | |
514 | /* At this point both ports hold the connection lock */ |
515 | |
516 | /* Ensure that the buffer numbers and sizes used are the maxima between connected ports. */ |
517 | buffer_num = MMAL_MAX(port->buffer_num, connected_port->buffer_num); |
518 | buffer_size = MMAL_MAX(port->buffer_size, connected_port->buffer_size); |
519 | port->buffer_num = connected_port->buffer_num = buffer_num; |
520 | port->buffer_size = connected_port->buffer_size = buffer_size; |
521 | |
522 | if (output->capabilities & MMAL_PORT_CAPABILITY_PASSTHROUGH) |
523 | buffer_size = 0; |
524 | |
525 | if (!port->priv->core->core_owns_connection) |
526 | return MMAL_SUCCESS; |
527 | |
528 | pool = mmal_port_pool_create(pool_port, buffer_num, buffer_size); |
529 | if (!pool) |
530 | { |
531 | LOG_ERROR("failed to create pool for connection" ); |
532 | return MMAL_ENOMEM; |
533 | } |
534 | |
535 | pool_core->pool_for_connection = pool; |
536 | mmal_pool_callback_set(pool, mmal_port_connected_pool_cb, output); |
537 | return MMAL_SUCCESS; |
538 | } |
539 | |
540 | static MMAL_STATUS_T mmal_port_connection_disable(MMAL_PORT_T *port, MMAL_PORT_T *connected_port) |
541 | { |
542 | MMAL_POOL_T *pool = port->priv->core->pool_for_connection ? |
543 | port->priv->core->pool_for_connection : connected_port->priv->core->pool_for_connection; |
544 | |
545 | mmal_pool_destroy(pool); |
546 | port->priv->core->pool_for_connection = |
547 | connected_port->priv->core->pool_for_connection = NULL; |
548 | return MMAL_SUCCESS; |
549 | } |
550 | |
551 | static MMAL_STATUS_T mmal_port_connection_start(MMAL_PORT_T *port, MMAL_PORT_T *connected_port) |
552 | { |
553 | MMAL_PORT_T *output = port->type == MMAL_PORT_TYPE_OUTPUT ? port : connected_port; |
554 | MMAL_PORT_T *input = connected_port->type == MMAL_PORT_TYPE_INPUT ? connected_port : port; |
555 | MMAL_POOL_T *pool = port->priv->core->pool_for_connection ? |
556 | port->priv->core->pool_for_connection : connected_port->priv->core->pool_for_connection; |
557 | MMAL_STATUS_T status; |
558 | |
559 | if (output->type == MMAL_PORT_TYPE_CLOCK && input->type == MMAL_PORT_TYPE_CLOCK) |
560 | { |
561 | /* Clock ports need buffers to send clock updates, so |
562 | * populate both clock ports */ |
563 | status = mmal_port_populate_clock_ports(output, input, pool); |
564 | } |
565 | else |
566 | { |
567 | /* Put the buffers into the output port */ |
568 | status = mmal_port_populate_from_pool(output, pool); |
569 | } |
570 | |
571 | return status; |
572 | } |
573 | |
574 | /** Disable processing on a port */ |
575 | MMAL_STATUS_T mmal_port_disable(MMAL_PORT_T *port) |
576 | { |
577 | MMAL_STATUS_T status; |
578 | MMAL_PORT_T *connected_port; |
579 | MMAL_PORT_PRIVATE_CORE_T *core; |
580 | |
581 | if (!port || !port->priv) |
582 | return MMAL_EINVAL; |
583 | |
584 | LOG_TRACE("%s(%i:%i) port %p" , port->component->name, |
585 | (int)port->type, (int)port->index, port); |
586 | |
587 | if (!port->priv->pf_disable) |
588 | return MMAL_ENOSYS; |
589 | |
590 | core = port->priv->core; |
591 | LOCK_CONNECTION(port); |
592 | connected_port = core->connected_port; |
593 | |
594 | /* Sanity checking */ |
595 | if (!port->is_enabled) |
596 | { |
597 | UNLOCK_CONNECTION(port); |
598 | LOG_ERROR("port %s(%p) is not enabled" , port->name, port); |
599 | return MMAL_EINVAL; |
600 | } |
601 | |
602 | if (connected_port) |
603 | LOCK_CONNECTION(connected_port); |
604 | |
605 | /* Disable the output port of a connection first */ |
606 | if (connected_port && connected_port->type != MMAL_PORT_TYPE_INPUT) |
607 | { |
608 | status = mmal_port_disable_internal(connected_port); |
609 | if (status != MMAL_SUCCESS) |
610 | { |
611 | LOG_ERROR("failed to disable connected port (%s)%p (%s)" , connected_port->name, |
612 | connected_port, mmal_status_to_string(status)); |
613 | goto end; |
614 | } |
615 | } |
616 | |
617 | status = mmal_port_disable_internal(port); |
618 | if (status != MMAL_SUCCESS) |
619 | { |
620 | LOG_ERROR("failed to disable port (%s)%p" , port->name, port); |
621 | goto end; |
622 | } |
623 | |
624 | /* Disable the input port of a connection last */ |
625 | if (connected_port && connected_port->type == MMAL_PORT_TYPE_INPUT) |
626 | { |
627 | status = mmal_port_disable_internal(connected_port); |
628 | if (status != MMAL_SUCCESS) |
629 | { |
630 | LOG_ERROR("failed to disable connected port (%s)%p (%s)" , connected_port->name, |
631 | connected_port, mmal_status_to_string(status)); |
632 | goto end; |
633 | } |
634 | } |
635 | |
636 | if (connected_port) |
637 | { |
638 | status = mmal_port_connection_disable(port, connected_port); |
639 | if (status != MMAL_SUCCESS) |
640 | LOG_ERROR("failed to disable connection (%s)%p (%s)" , port->name, |
641 | port, mmal_status_to_string(status)); |
642 | } |
643 | |
644 | end: |
645 | if (connected_port) |
646 | UNLOCK_CONNECTION(connected_port); |
647 | UNLOCK_CONNECTION(port); |
648 | |
649 | return status; |
650 | } |
651 | |
652 | static MMAL_STATUS_T mmal_port_disable_internal(MMAL_PORT_T *port) |
653 | { |
654 | MMAL_PORT_PRIVATE_CORE_T* core = port->priv->core; |
655 | MMAL_STATUS_T status = MMAL_SUCCESS; |
656 | MMAL_BUFFER_HEADER_T *buffer; |
657 | |
658 | LOCK_PORT(port); |
659 | |
660 | if (!port->is_enabled) |
661 | goto end; |
662 | |
663 | LOCK_SENDING(port); |
664 | port->is_enabled = 0; |
665 | UNLOCK_SENDING(port); |
666 | |
667 | mmal_component_action_lock(port->component); |
668 | |
669 | if (core->pool_for_connection) |
670 | mmal_pool_callback_set(core->pool_for_connection, NULL, NULL); |
671 | |
672 | status = port->priv->pf_disable(port); |
673 | |
674 | mmal_component_action_unlock(port->component); |
675 | |
676 | if (status != MMAL_SUCCESS) |
677 | { |
678 | LOG_ERROR("port %p could not be disabled (%s)" , port->name, mmal_status_to_string(status)); |
679 | LOCK_SENDING(port); |
680 | port->is_enabled = 1; |
681 | UNLOCK_SENDING(port); |
682 | goto end; |
683 | } |
684 | |
685 | /* Flush our internal queue */ |
686 | buffer = port->priv->core->queue_first; |
687 | while (buffer) |
688 | { |
689 | MMAL_BUFFER_HEADER_T *next = buffer->next; |
690 | mmal_port_buffer_header_callback(port, buffer); |
691 | buffer = next; |
692 | } |
693 | port->priv->core->queue_first = 0; |
694 | port->priv->core->queue_last = &port->priv->core->queue_first; |
695 | |
696 | /* Wait for all the buffers to have come back from the component */ |
697 | LOG_DEBUG("%s waiting for %i buffers left in transit" , port->name, (int)IN_TRANSIT_COUNT(port)); |
698 | IN_TRANSIT_WAIT(port); |
699 | LOG_DEBUG("%s has no buffers left in transit" , port->name); |
700 | |
701 | port->priv->core->buffer_header_callback = NULL; |
702 | |
703 | end: |
704 | UNLOCK_PORT(port); |
705 | return status; |
706 | } |
707 | |
708 | /** Send a buffer header to a port */ |
709 | MMAL_STATUS_T mmal_port_send_buffer(MMAL_PORT_T *port, |
710 | MMAL_BUFFER_HEADER_T *buffer) |
711 | { |
712 | MMAL_STATUS_T status = MMAL_SUCCESS; |
713 | |
714 | if (!port || !port->priv) |
715 | { |
716 | LOG_ERROR("invalid port" ); |
717 | return MMAL_EINVAL; |
718 | } |
719 | |
720 | #ifdef ENABLE_MMAL_EXTRA_LOGGING |
721 | LOG_TRACE("%s(%i:%i) port %p, buffer %p (%p,%i,%i)" , |
722 | port->component->name, (int)port->type, (int)port->index, port, buffer, |
723 | buffer ? buffer->data: 0, buffer ? (int)buffer->offset : 0, |
724 | buffer ? (int)buffer->length : 0); |
725 | #endif |
726 | |
727 | if (buffer->alloc_size && !buffer->data && |
728 | !(port->capabilities & MMAL_PORT_CAPABILITY_PASSTHROUGH)) |
729 | { |
730 | LOG_ERROR("%s(%p) received invalid buffer header" , port->name, port); |
731 | return MMAL_EINVAL; |
732 | } |
733 | |
734 | if (!port->priv->pf_send) |
735 | return MMAL_ENOSYS; |
736 | |
737 | LOCK_SENDING(port); |
738 | |
739 | if (!port->is_enabled) |
740 | { |
741 | UNLOCK_SENDING(port); |
742 | return MMAL_EINVAL; |
743 | } |
744 | |
745 | if (port->type == MMAL_PORT_TYPE_OUTPUT && buffer->length) |
746 | { |
747 | LOG_DEBUG("given an output buffer with length != 0" ); |
748 | buffer->length = 0; |
749 | } |
750 | |
751 | /* coverity[lock] transit_sema is used for signalling, and is not a lock */ |
752 | /* coverity[lock_order] since transit_sema is not a lock, there is no ordering conflict */ |
753 | IN_TRANSIT_INCREMENT(port); |
754 | |
755 | if (port->priv->core->is_paused) |
756 | { |
757 | /* Add buffer to our internal queue */ |
758 | buffer->next = NULL; |
759 | *port->priv->core->queue_last = buffer; |
760 | port->priv->core->queue_last = &buffer->next; |
761 | } |
762 | else |
763 | { |
764 | /* Send buffer to component */ |
765 | status = port->priv->pf_send(port, buffer); |
766 | } |
767 | |
768 | if (status != MMAL_SUCCESS) |
769 | { |
770 | IN_TRANSIT_DECREMENT(port); |
771 | LOG_ERROR("%s: send failed: %s" , port->name, mmal_status_to_string(status)); |
772 | } |
773 | else |
774 | { |
775 | mmal_port_update_port_stats(port, MMAL_CORE_STATS_RX); |
776 | } |
777 | |
778 | UNLOCK_SENDING(port); |
779 | return status; |
780 | } |
781 | |
782 | /** Flush a port */ |
783 | MMAL_STATUS_T mmal_port_flush(MMAL_PORT_T *port) |
784 | { |
785 | MMAL_BUFFER_HEADER_T *buffer = 0; |
786 | MMAL_STATUS_T status; |
787 | |
788 | if (!port || !port->priv) |
789 | return MMAL_EINVAL; |
790 | |
791 | LOG_TRACE("%s(%i:%i) port %p" , port->component->name, |
792 | (int)port->type, (int)port->index, port); |
793 | |
794 | if (!port->priv->pf_flush) |
795 | return MMAL_ENOSYS; |
796 | |
797 | /* N.B. must take action lock *before* sending lock */ |
798 | mmal_component_action_lock(port->component); |
799 | LOCK_SENDING(port); |
800 | |
801 | if (!port->is_enabled) |
802 | { |
803 | UNLOCK_SENDING(port); |
804 | mmal_component_action_unlock(port->component); |
805 | return MMAL_SUCCESS; |
806 | } |
807 | |
808 | status = port->priv->pf_flush(port); |
809 | if (status == MMAL_SUCCESS) |
810 | { |
811 | /* Flush our internal queue */ |
812 | buffer = port->priv->core->queue_first; |
813 | port->priv->core->queue_first = 0; |
814 | port->priv->core->queue_last = &port->priv->core->queue_first; |
815 | } |
816 | |
817 | UNLOCK_SENDING(port); |
818 | mmal_component_action_unlock(port->component); |
819 | |
820 | while (buffer) |
821 | { |
822 | MMAL_BUFFER_HEADER_T *next = buffer->next; |
823 | mmal_port_buffer_header_callback(port, buffer); |
824 | buffer = next; |
825 | } |
826 | return status; |
827 | } |
828 | |
829 | /* Set a parameter on a port. */ |
830 | MMAL_STATUS_T mmal_port_parameter_set(MMAL_PORT_T *port, |
831 | const MMAL_PARAMETER_HEADER_T *param) |
832 | { |
833 | MMAL_STATUS_T status = MMAL_ENOSYS; |
834 | |
835 | if (!port) |
836 | { |
837 | LOG_ERROR("no port" ); |
838 | return MMAL_EINVAL; |
839 | } |
840 | if (!param) |
841 | { |
842 | LOG_ERROR("param not supplied" ); |
843 | return MMAL_EINVAL; |
844 | } |
845 | if (!port->priv) |
846 | { |
847 | LOG_ERROR("port not configured" ); |
848 | return MMAL_EINVAL; |
849 | } |
850 | |
851 | LOG_TRACE("%s(%i:%i) port %p, param %p (%x,%i)" , port->component->name, |
852 | (int)port->type, (int)port->index, port, |
853 | param, param ? param->id : 0, param ? (int)param->size : 0); |
854 | |
855 | LOCK_PORT(port); |
856 | if (port->priv->pf_parameter_set) |
857 | status = port->priv->pf_parameter_set(port, param); |
858 | if (status == MMAL_ENOSYS) |
859 | { |
860 | /* is this a core parameter? */ |
861 | status = mmal_port_private_parameter_set(port, param); |
862 | } |
863 | UNLOCK_PORT(port); |
864 | return status; |
865 | } |
866 | |
867 | /* Get a port parameter */ |
868 | MMAL_STATUS_T mmal_port_parameter_get(MMAL_PORT_T *port, |
869 | MMAL_PARAMETER_HEADER_T *param) |
870 | { |
871 | MMAL_STATUS_T status = MMAL_ENOSYS; |
872 | |
873 | if (!port || !port->priv) |
874 | return MMAL_EINVAL; |
875 | |
876 | LOG_TRACE("%s(%i:%i) port %p, param %p (%x,%i)" , port->component->name, |
877 | (int)port->type, (int)port->index, port, |
878 | param, param ? param->id : 0, param ? (int)param->size : 0); |
879 | |
880 | if (!param) |
881 | return MMAL_EINVAL; |
882 | |
883 | LOCK_PORT(port); |
884 | if (port->priv->pf_parameter_get) |
885 | status = port->priv->pf_parameter_get(port, param); |
886 | if (status == MMAL_ENOSYS) |
887 | { |
888 | /* is this a core parameter? */ |
889 | status = mmal_port_private_parameter_get(port, param); |
890 | } |
891 | |
892 | UNLOCK_PORT(port); |
893 | return status; |
894 | } |
895 | |
896 | /** Buffer header callback. */ |
897 | void (MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
898 | { |
899 | #ifdef ENABLE_MMAL_EXTRA_LOGGING |
900 | LOG_TRACE("%s(%i:%i) port %p, buffer %p (%i,%p,%i,%i)" , |
901 | port->component->name, (int)port->type, (int)port->index, port, buffer, |
902 | buffer ? (int)buffer->cmd : 0, buffer ? buffer->data : 0, |
903 | buffer ? (int)buffer->offset : 0, buffer ? (int)buffer->length : 0); |
904 | #endif |
905 | |
906 | if (!vcos_verify(IN_TRANSIT_COUNT(port) >= 0)) |
907 | LOG_ERROR("%s: buffer headers in transit < 0 (%d)" , port->name, (int)IN_TRANSIT_COUNT(port)); |
908 | |
909 | if (MMAL_COLLECT_PORT_STATS_ENABLED) |
910 | { |
911 | mmal_port_update_port_stats(port, MMAL_CORE_STATS_TX); |
912 | } |
913 | |
914 | port->priv->core->buffer_header_callback(port, buffer); |
915 | |
916 | IN_TRANSIT_DECREMENT(port); |
917 | } |
918 | |
919 | /** Event callback */ |
920 | void mmal_port_event_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
921 | { |
922 | if (port->priv->core->buffer_header_callback) |
923 | { |
924 | port->priv->core->buffer_header_callback(port, buffer); |
925 | } |
926 | else |
927 | { |
928 | LOG_ERROR("event lost on port %i,%i (buffer header callback not defined)" , |
929 | (int)port->type, (int)port->index); |
930 | mmal_buffer_header_release(buffer); |
931 | } |
932 | } |
933 | |
934 | /** Connect an output port to an input port. */ |
935 | MMAL_STATUS_T mmal_port_connect(MMAL_PORT_T *port, MMAL_PORT_T *other_port) |
936 | { |
937 | MMAL_PORT_PRIVATE_CORE_T* core; |
938 | MMAL_PORT_PRIVATE_CORE_T* other_core; |
939 | MMAL_STATUS_T status = MMAL_SUCCESS; |
940 | MMAL_PORT_T* output_port = NULL; |
941 | |
942 | if (!port || !port->priv || !other_port || !other_port->priv) |
943 | { |
944 | LOG_ERROR("invalid port" ); |
945 | return MMAL_EINVAL; |
946 | } |
947 | |
948 | if ((port->type == MMAL_PORT_TYPE_CLOCK) && (port->type != other_port->type)) |
949 | { |
950 | LOG_ERROR("invalid port connection" ); |
951 | return MMAL_EINVAL; |
952 | } |
953 | |
954 | LOG_TRACE("connecting %s(%p) to %s(%p)" , port->name, port, other_port->name, other_port); |
955 | |
956 | if (!port->priv->pf_connect || !other_port->priv->pf_connect) |
957 | { |
958 | LOG_ERROR("at least one pf_connect is NULL" ); |
959 | return MMAL_ENOSYS; |
960 | } |
961 | |
962 | core = port->priv->core; |
963 | other_core = other_port->priv->core; |
964 | |
965 | LOCK_CONNECTION(port); |
966 | if (core->connected_port) |
967 | { |
968 | LOG_ERROR("port %p is already connected" , port); |
969 | UNLOCK_CONNECTION(port); |
970 | return MMAL_EISCONN; |
971 | } |
972 | if (port->is_enabled) |
973 | { |
974 | LOG_ERROR("port %p should not be enabled" , port); |
975 | UNLOCK_CONNECTION(port); |
976 | return MMAL_EINVAL; |
977 | } |
978 | |
979 | LOCK_CONNECTION(other_port); |
980 | if (other_core->connected_port) |
981 | { |
982 | LOG_ERROR("port %p is already connected" , other_port); |
983 | status = MMAL_EISCONN; |
984 | goto finish; |
985 | } |
986 | if (other_port->is_enabled) |
987 | { |
988 | LOG_ERROR("port %p should not be enabled" , other_port); |
989 | status = MMAL_EINVAL; |
990 | goto finish; |
991 | } |
992 | |
993 | core->connected_port = other_port; |
994 | other_core->connected_port = port; |
995 | |
996 | core->core_owns_connection = 0; |
997 | other_core->core_owns_connection = 0; |
998 | |
999 | /* Check to see if the port will manage the connection on its own. If not then the core |
1000 | * will manage it. */ |
1001 | output_port = port->type == MMAL_PORT_TYPE_OUTPUT ? port : other_port; |
1002 | if (output_port->priv->pf_connect(port, other_port) == MMAL_SUCCESS) |
1003 | goto finish; |
1004 | |
1005 | core->core_owns_connection = 1; |
1006 | other_core->core_owns_connection = 1; |
1007 | |
1008 | |
1009 | finish: |
1010 | UNLOCK_CONNECTION(other_port); |
1011 | UNLOCK_CONNECTION(port); |
1012 | return status; |
1013 | } |
1014 | |
1015 | /** Disconnect a connected port. */ |
1016 | MMAL_STATUS_T mmal_port_disconnect(MMAL_PORT_T *port) |
1017 | { |
1018 | MMAL_PORT_PRIVATE_CORE_T* core; |
1019 | MMAL_PORT_T* other_port; |
1020 | MMAL_STATUS_T status = MMAL_SUCCESS; |
1021 | |
1022 | if (!port || !port->priv) |
1023 | { |
1024 | LOG_ERROR("invalid port" ); |
1025 | return MMAL_EINVAL; |
1026 | } |
1027 | |
1028 | LOG_TRACE("%s(%p)" , port->name, port); |
1029 | |
1030 | LOCK_CONNECTION(port); |
1031 | |
1032 | core = port->priv->core; |
1033 | other_port = core->connected_port; |
1034 | |
1035 | if (!other_port) |
1036 | { |
1037 | UNLOCK_CONNECTION(port); |
1038 | LOG_DEBUG("%s(%p) is not connected" , port->name, port); |
1039 | return MMAL_ENOTCONN; |
1040 | } |
1041 | |
1042 | LOCK_CONNECTION(other_port); |
1043 | |
1044 | /* Make sure the connection is disabled first */ |
1045 | if (port->is_enabled) |
1046 | { |
1047 | MMAL_PORT_T *output = port->type == MMAL_PORT_TYPE_OUTPUT ? port : other_port; |
1048 | MMAL_PORT_T *input = other_port->type == MMAL_PORT_TYPE_INPUT ? other_port : port; |
1049 | |
1050 | status = mmal_port_disable_internal(output); |
1051 | if (status != MMAL_SUCCESS) |
1052 | { |
1053 | LOG_ERROR("failed to disable port (%s)%p" , port->name, port); |
1054 | goto end; |
1055 | } |
1056 | status = mmal_port_disable_internal(input); |
1057 | if (status != MMAL_SUCCESS) |
1058 | { |
1059 | LOG_ERROR("failed to disable port (%s)%p" , port->name, port); |
1060 | goto end; |
1061 | } |
1062 | status = mmal_port_connection_disable(port, other_port); |
1063 | } |
1064 | |
1065 | if (!core->core_owns_connection) |
1066 | { |
1067 | status = port->priv->pf_connect(port, NULL); |
1068 | if (status != MMAL_SUCCESS) |
1069 | { |
1070 | LOG_ERROR("disconnection of %s(%p) failed (%i)" , port->name, port, status); |
1071 | goto end; |
1072 | } |
1073 | } |
1074 | |
1075 | core->connected_port = NULL; |
1076 | other_port->priv->core->connected_port = NULL; |
1077 | |
1078 | end: |
1079 | UNLOCK_CONNECTION(other_port); |
1080 | UNLOCK_CONNECTION(port); |
1081 | return status; |
1082 | } |
1083 | |
1084 | /** Allocate a payload buffer */ |
1085 | uint8_t *mmal_port_payload_alloc(MMAL_PORT_T *port, uint32_t payload_size) |
1086 | { |
1087 | uint8_t *mem; |
1088 | |
1089 | if (!port || !port->priv) |
1090 | return NULL; |
1091 | |
1092 | LOG_TRACE("%s(%i:%i) port %p, size %i" , port->component->name, |
1093 | (int)port->type, (int)port->index, port, (int)payload_size); |
1094 | |
1095 | if (!payload_size) |
1096 | return NULL; |
1097 | |
1098 | /* TODO: keep track of the allocs so we can free them when the component is destroyed */ |
1099 | |
1100 | if (!port->priv->pf_payload_alloc) |
1101 | { |
1102 | /* Revert to using the heap */ |
1103 | #ifdef _VIDEOCORE |
1104 | mem = (void *)mem_alloc(payload_size, 32, MEM_FLAG_DIRECT, port->name); |
1105 | #else |
1106 | mem = vcos_malloc(payload_size, "mmal payload" ); |
1107 | #endif |
1108 | goto end; |
1109 | } |
1110 | |
1111 | LOCK_PORT(port); |
1112 | mem = port->priv->pf_payload_alloc(port, payload_size); |
1113 | UNLOCK_PORT(port); |
1114 | |
1115 | end: |
1116 | /* Acquire the port if the allocation was successful. |
1117 | * This will ensure that the component is not destroyed until the payload has been freed. */ |
1118 | if (mem) |
1119 | mmal_port_acquire(port); |
1120 | return mem; |
1121 | } |
1122 | |
1123 | /** Free a payload buffer */ |
1124 | void mmal_port_payload_free(MMAL_PORT_T *port, uint8_t *payload) |
1125 | { |
1126 | if (!port || !port->priv) |
1127 | return; |
1128 | |
1129 | LOG_TRACE("%s(%i:%i) port %p, payload %p" , port->component->name, |
1130 | (int)port->type, (int)port->index, port, payload); |
1131 | |
1132 | if (!port->priv->pf_payload_alloc) |
1133 | { |
1134 | /* Revert to using the heap */ |
1135 | #ifdef _VIDEOCORE |
1136 | mem_release((MEM_HANDLE_T)payload); |
1137 | #else |
1138 | vcos_free(payload); |
1139 | #endif |
1140 | mmal_port_release(port); |
1141 | return; |
1142 | } |
1143 | |
1144 | LOCK_PORT(port); |
1145 | port->priv->pf_payload_free(port, payload); |
1146 | UNLOCK_PORT(port); |
1147 | mmal_port_release(port); |
1148 | } |
1149 | |
1150 | MMAL_STATUS_T mmal_port_event_get(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T **buffer, uint32_t event) |
1151 | { |
1152 | if (!port || !port->priv || !buffer) |
1153 | return MMAL_EINVAL; |
1154 | |
1155 | LOG_TRACE("%s(%i:%i) port %p, event %4.4s" , port->component->name, |
1156 | (int)port->type, (int)port->index, port, (char *)&event); |
1157 | |
1158 | /* Get an event buffer from our event pool */ |
1159 | *buffer = mmal_queue_get(port->component->priv->event_pool->queue); |
1160 | if (!*buffer) |
1161 | { |
1162 | LOG_ERROR("%s(%i:%i) port %p, no event buffer left for %4.4s" , port->component->name, |
1163 | (int)port->type, (int)port->index, port, (char *)&event); |
1164 | return MMAL_ENOSPC; |
1165 | } |
1166 | |
1167 | (*buffer)->cmd = event; |
1168 | (*buffer)->length = 0; |
1169 | |
1170 | /* Special case for the FORMAT_CHANGED event. We need to properly initialise the event |
1171 | * buffer so that it contains an initialised MMAL_ES_FORMAT_T structure. */ |
1172 | if (event == MMAL_EVENT_FORMAT_CHANGED) |
1173 | { |
1174 | uint32_t size = sizeof(MMAL_EVENT_FORMAT_CHANGED_T); |
1175 | size += sizeof(MMAL_ES_FORMAT_T) + sizeof(MMAL_ES_SPECIFIC_FORMAT_T); |
1176 | |
1177 | if ((*buffer)->alloc_size < size) |
1178 | { |
1179 | LOG_ERROR("%s(%i:%i) port %p, event buffer for %4.4s is too small (%i/%i)" , |
1180 | port->component->name, (int)port->type, (int)port->index, port, |
1181 | (char *)&event, (int)(*buffer)->alloc_size, (int)size); |
1182 | goto error; |
1183 | } |
1184 | |
1185 | memset((*buffer)->data, 0, size); |
1186 | (*buffer)->length = size; |
1187 | } |
1188 | |
1189 | return MMAL_SUCCESS; |
1190 | |
1191 | error: |
1192 | if (*buffer) |
1193 | mmal_buffer_header_release(*buffer); |
1194 | *buffer = NULL; |
1195 | return MMAL_ENOSPC; |
1196 | } |
1197 | |
1198 | /** Populate clock ports from the given pool */ |
1199 | static MMAL_STATUS_T mmal_port_populate_clock_ports(MMAL_PORT_T* output, MMAL_PORT_T* input, MMAL_POOL_T* pool) |
1200 | { |
1201 | MMAL_STATUS_T status = MMAL_SUCCESS; |
1202 | MMAL_BUFFER_HEADER_T *buffer; |
1203 | |
1204 | if (!output->priv->pf_send || !input->priv->pf_send) |
1205 | return MMAL_ENOSYS; |
1206 | |
1207 | LOG_TRACE("output %s %p, input %s %p, pool: %p" , output->name, output, input->name, input, pool); |
1208 | |
1209 | buffer = mmal_queue_get(pool->queue); |
1210 | while (buffer) |
1211 | { |
1212 | status = mmal_port_send_buffer(output, buffer); |
1213 | if (status != MMAL_SUCCESS) |
1214 | { |
1215 | LOG_ERROR("failed to send buffer to clock port %s" , output->name); |
1216 | mmal_buffer_header_release(buffer); |
1217 | break; |
1218 | } |
1219 | |
1220 | buffer = mmal_queue_get(pool->queue); |
1221 | if (buffer) |
1222 | { |
1223 | status = mmal_port_send_buffer(input, buffer); |
1224 | if (status != MMAL_SUCCESS) |
1225 | { |
1226 | LOG_ERROR("failed to send buffer to clock port %s" , output->name); |
1227 | mmal_buffer_header_release(buffer); |
1228 | break; |
1229 | } |
1230 | buffer = mmal_queue_get(pool->queue); |
1231 | } |
1232 | } |
1233 | |
1234 | return status; |
1235 | } |
1236 | |
1237 | /** Populate an output port with a pool of buffers */ |
1238 | static MMAL_STATUS_T mmal_port_populate_from_pool(MMAL_PORT_T* port, MMAL_POOL_T* pool) |
1239 | { |
1240 | MMAL_STATUS_T status = MMAL_SUCCESS; |
1241 | uint32_t buffer_idx; |
1242 | MMAL_BUFFER_HEADER_T *buffer; |
1243 | |
1244 | if (!port->priv->pf_send) |
1245 | return MMAL_ENOSYS; |
1246 | |
1247 | LOG_TRACE("%s port %p, pool: %p" , port->name, port, pool); |
1248 | |
1249 | /* Populate port from pool */ |
1250 | for (buffer_idx = 0; buffer_idx < port->buffer_num; buffer_idx++) |
1251 | { |
1252 | buffer = mmal_queue_get(pool->queue); |
1253 | if (!buffer) |
1254 | { |
1255 | LOG_ERROR("too few buffers in the pool" ); |
1256 | status = MMAL_ENOMEM; |
1257 | break; |
1258 | } |
1259 | |
1260 | status = mmal_port_send_buffer(port, buffer); |
1261 | if (status != MMAL_SUCCESS) |
1262 | { |
1263 | LOG_ERROR("failed to send buffer to port" ); |
1264 | mmal_buffer_header_release(buffer); |
1265 | break; |
1266 | } |
1267 | } |
1268 | |
1269 | return status; |
1270 | } |
1271 | |
1272 | /** Default behaviour when setting up or tearing down a connection to another port */ |
1273 | static MMAL_STATUS_T mmal_port_connect_default(MMAL_PORT_T *port, MMAL_PORT_T *other_port) |
1274 | { |
1275 | MMAL_PARAM_UNUSED(port); |
1276 | MMAL_PARAM_UNUSED(other_port); |
1277 | |
1278 | LOG_TRACE("port %p, other_port %p" , port, other_port); |
1279 | return MMAL_ENOSYS; |
1280 | } |
1281 | |
1282 | /** Connected input port buffer callback */ |
1283 | static void mmal_port_connected_input_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
1284 | { |
1285 | LOG_TRACE("buffer %p from connected input port %p: data %p, alloc_size %u, length %u" , |
1286 | buffer, port, buffer->data, buffer->alloc_size, buffer->length); |
1287 | |
1288 | /* Clock ports are bi-directional and a buffer coming from an "input" |
1289 | * clock port can potentially have valid payload data, in which case |
1290 | * it should be sent directly to the connected port. */ |
1291 | if (port->type == MMAL_PORT_TYPE_CLOCK && buffer->length) |
1292 | { |
1293 | MMAL_PORT_T* connected_port = port->priv->core->connected_port; |
1294 | mmal_port_send_buffer(connected_port, buffer); |
1295 | return; |
1296 | } |
1297 | |
1298 | /* Simply release buffer back into pool for re-use */ |
1299 | mmal_buffer_header_release(buffer); |
1300 | } |
1301 | |
1302 | /** Connected output port buffer callback */ |
1303 | static void mmal_port_connected_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
1304 | { |
1305 | MMAL_PORT_T* connected_port = port->priv->core->connected_port; |
1306 | MMAL_STATUS_T status; |
1307 | |
1308 | LOG_TRACE("buffer %p from connected output port %p: data %p, alloc_size %u, length %u" , |
1309 | buffer, port, buffer->data, buffer->alloc_size, buffer->length); |
1310 | |
1311 | if (buffer->cmd) |
1312 | { |
1313 | MMAL_EVENT_FORMAT_CHANGED_T *event = mmal_event_format_changed_get(buffer); |
1314 | |
1315 | /* Handle format changed events */ |
1316 | if (event) |
1317 | { |
1318 | /* Apply the change */ |
1319 | status = mmal_format_full_copy(port->format, event->format); |
1320 | if (status == MMAL_SUCCESS) |
1321 | status = mmal_port_format_commit(port); |
1322 | if (status != MMAL_SUCCESS) |
1323 | LOG_ERROR("format commit failed on port %s (%i)" , port->name, status); |
1324 | |
1325 | /* Forward to the connected port */ |
1326 | if (status == MMAL_SUCCESS) |
1327 | status = mmal_port_send_buffer(connected_port, buffer); |
1328 | |
1329 | if (status != MMAL_SUCCESS) |
1330 | { |
1331 | mmal_event_error_send(port->component, status); |
1332 | mmal_buffer_header_release(buffer); |
1333 | } |
1334 | return; /* Event handled */ |
1335 | } |
1336 | |
1337 | /* FIXME Release other event buffers for now, until we can deal with shared memory issues */ |
1338 | mmal_buffer_header_release(buffer); |
1339 | } |
1340 | else |
1341 | { |
1342 | if (port->is_enabled) |
1343 | { |
1344 | /* Forward data buffers to the connected input port */ |
1345 | status = mmal_port_send_buffer(connected_port, buffer); |
1346 | if (status != MMAL_SUCCESS) |
1347 | { |
1348 | LOG_ERROR("%s could not send buffer on port %s (%s)" , |
1349 | port->name, connected_port->name, mmal_status_to_string(status)); |
1350 | mmal_buffer_header_release(buffer); |
1351 | } |
1352 | } |
1353 | else |
1354 | { |
1355 | /* This port is disabled. Buffer will be a flushed buffer, so |
1356 | * return to the pool rather than delivering it. |
1357 | */ |
1358 | mmal_buffer_header_release(buffer); |
1359 | } |
1360 | } |
1361 | } |
1362 | |
1363 | /** Callback for when a buffer from a connected input port is finally released */ |
1364 | static MMAL_BOOL_T mmal_port_connected_pool_cb(MMAL_POOL_T *pool, MMAL_BUFFER_HEADER_T *buffer, void *userdata) |
1365 | { |
1366 | MMAL_PORT_T* port = (MMAL_PORT_T*)userdata; |
1367 | MMAL_STATUS_T status; |
1368 | MMAL_PARAM_UNUSED(pool); |
1369 | |
1370 | LOG_TRACE("released buffer %p, data %p alloc_size %u length %u" , |
1371 | buffer, buffer->data, buffer->alloc_size, buffer->length); |
1372 | |
1373 | /* Pipe the buffer back to the output port */ |
1374 | status = mmal_port_send_buffer(port, buffer); |
1375 | |
1376 | /* Put the buffer back in the pool if we were successful */ |
1377 | return status != MMAL_SUCCESS; |
1378 | } |
1379 | |
1380 | /*****************************************************************************/ |
1381 | static void mmal_port_name_update(MMAL_PORT_T *port) |
1382 | { |
1383 | MMAL_PORT_PRIVATE_CORE_T* core = port->priv->core; |
1384 | |
1385 | vcos_snprintf(core->name, core->name_size - 1, PORT_NAME_FORMAT, |
1386 | port->component->name, mmal_port_type_to_string(port->type), (int)port->index, |
1387 | port->format && port->format->encoding ? '(' : '\0', |
1388 | port->format && port->format->encoding ? (char *)&port->format->encoding : "" ); |
1389 | } |
1390 | |
1391 | static MMAL_STATUS_T mmal_port_get_core_stats(MMAL_PORT_T *port, MMAL_PARAMETER_HEADER_T *param) |
1392 | { |
1393 | MMAL_PARAMETER_CORE_STATISTICS_T *stats_param = (MMAL_PARAMETER_CORE_STATISTICS_T*)param; |
1394 | MMAL_CORE_STATISTICS_T *stats = &stats_param->stats; |
1395 | MMAL_CORE_STATISTICS_T *src_stats; |
1396 | MMAL_PORT_PRIVATE_CORE_T *core = port->priv->core; |
1397 | vcos_mutex_lock(&core->stats_lock); |
1398 | switch (stats_param->dir) |
1399 | { |
1400 | case MMAL_CORE_STATS_RX: |
1401 | src_stats = &port->priv->core->stats.rx; |
1402 | break; |
1403 | default: |
1404 | src_stats = &port->priv->core->stats.tx; |
1405 | break; |
1406 | } |
1407 | *stats = *src_stats; |
1408 | if (stats_param->reset) |
1409 | memset(src_stats, 0, sizeof(*src_stats)); |
1410 | vcos_mutex_unlock(&core->stats_lock); |
1411 | return MMAL_SUCCESS; |
1412 | } |
1413 | |
1414 | /** Update the port stats, called per buffer. |
1415 | * |
1416 | */ |
1417 | static void mmal_port_update_port_stats(MMAL_PORT_T *port, MMAL_CORE_STATS_DIR direction) |
1418 | { |
1419 | MMAL_PORT_PRIVATE_CORE_T *core = port->priv->core; |
1420 | MMAL_CORE_STATISTICS_T *stats; |
1421 | unsigned stc = vcos_getmicrosecs(); |
1422 | |
1423 | vcos_mutex_lock(&core->stats_lock); |
1424 | |
1425 | stats = direction == MMAL_CORE_STATS_RX ? &core->stats.rx : &core->stats.tx; |
1426 | |
1427 | stats->buffer_count++; |
1428 | |
1429 | if (!stats->first_buffer_time) |
1430 | { |
1431 | stats->last_buffer_time = stats->first_buffer_time = stc; |
1432 | } |
1433 | else |
1434 | { |
1435 | stats->max_delay = vcos_max(stats->max_delay, stc-stats->last_buffer_time); |
1436 | stats->last_buffer_time = stc; |
1437 | } |
1438 | vcos_mutex_unlock(&core->stats_lock); |
1439 | } |
1440 | |
1441 | static MMAL_STATUS_T mmal_port_private_parameter_get(MMAL_PORT_T *port, |
1442 | MMAL_PARAMETER_HEADER_T *param) |
1443 | { |
1444 | switch (param->id) |
1445 | { |
1446 | case MMAL_PARAMETER_CORE_STATISTICS: |
1447 | return mmal_port_get_core_stats(port, param); |
1448 | default: |
1449 | return MMAL_ENOSYS; |
1450 | } |
1451 | } |
1452 | |
1453 | static MMAL_STATUS_T mmal_port_private_parameter_set(MMAL_PORT_T *port, |
1454 | const MMAL_PARAMETER_HEADER_T *param) |
1455 | { |
1456 | (void)port; |
1457 | switch (param->id) |
1458 | { |
1459 | default: |
1460 | return MMAL_ENOSYS; |
1461 | } |
1462 | } |
1463 | |
1464 | MMAL_STATUS_T mmal_port_pause(MMAL_PORT_T *port, MMAL_BOOL_T pause) |
1465 | { |
1466 | MMAL_STATUS_T status = MMAL_SUCCESS; |
1467 | |
1468 | LOCK_SENDING(port); |
1469 | |
1470 | /* When resuming from pause, we send all our queued buffers to the port */ |
1471 | if (!pause && port->is_enabled) |
1472 | { |
1473 | MMAL_BUFFER_HEADER_T *buffer = port->priv->core->queue_first; |
1474 | while (buffer) |
1475 | { |
1476 | MMAL_BUFFER_HEADER_T *next = buffer->next; |
1477 | status = port->priv->pf_send(port, buffer); |
1478 | if (status != MMAL_SUCCESS) |
1479 | { |
1480 | buffer->next = next; |
1481 | break; |
1482 | } |
1483 | buffer = next; |
1484 | } |
1485 | |
1486 | /* If for some reason we could not send one of the buffers, we just |
1487 | * leave all the buffers in our internal queue and return an error. */ |
1488 | if (status != MMAL_SUCCESS) |
1489 | { |
1490 | port->priv->core->queue_first = buffer; |
1491 | } |
1492 | else |
1493 | { |
1494 | port->priv->core->queue_first = 0; |
1495 | port->priv->core->queue_last = &port->priv->core->queue_first; |
1496 | } |
1497 | } |
1498 | |
1499 | if (status == MMAL_SUCCESS) |
1500 | port->priv->core->is_paused = pause; |
1501 | |
1502 | UNLOCK_SENDING(port); |
1503 | return status; |
1504 | } |
1505 | |
1506 | MMAL_BOOL_T mmal_port_is_connected(MMAL_PORT_T *port) |
1507 | { |
1508 | return !!port->priv->core->connected_port; |
1509 | } |
1510 | |