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 "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
51static MMAL_STATUS_T mmal_port_private_parameter_get(MMAL_PORT_T *port,
52 MMAL_PARAMETER_HEADER_T *param);
53
54static 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. */
61typedef 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 buffer_header_callback;
70
71 /** Keeps track of the number of buffer headers currently in transit in this port */
72 int32_t transit_buffer_headers;
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 *****************************************************************************/
105static MMAL_STATUS_T mmal_port_enable_internal(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb);
106static MMAL_STATUS_T mmal_port_disable_internal(MMAL_PORT_T *port);
107
108static MMAL_STATUS_T mmal_port_connection_enable(MMAL_PORT_T *port, MMAL_PORT_T *connected_port);
109static MMAL_STATUS_T mmal_port_connection_disable(MMAL_PORT_T *port, MMAL_PORT_T *connected_port);
110static MMAL_STATUS_T mmal_port_connection_start(MMAL_PORT_T *port, MMAL_PORT_T *connected_port);
111static MMAL_STATUS_T mmal_port_populate_from_pool(MMAL_PORT_T* port, MMAL_POOL_T* pool);
112static MMAL_STATUS_T mmal_port_populate_clock_ports(MMAL_PORT_T* output, MMAL_PORT_T* input, MMAL_POOL_T* pool);
113static MMAL_STATUS_T mmal_port_connect_default(MMAL_PORT_T *port, MMAL_PORT_T *other_port);
114static void mmal_port_connected_input_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
115static void mmal_port_connected_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
116static MMAL_BOOL_T mmal_port_connected_pool_cb(MMAL_POOL_T *pool, MMAL_BUFFER_HEADER_T *buffer, void *userdata);
117
118static void mmal_port_name_update(MMAL_PORT_T *port);
119static 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 */
158MMAL_PORT_T *mmal_port_alloc(MMAL_COMPONENT_T *component, MMAL_PORT_TYPE_T type, unsigned int extra_size)
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 */
228void 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 */
247MMAL_PORT_T **mmal_ports_alloc(MMAL_COMPONENT_T *component, unsigned int ports_num,
248 MMAL_PORT_TYPE_T type, unsigned int extra_size)
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 */
278void 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 */
288MMAL_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 */
351MMAL_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
453error:
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
467static 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
500end:
501 UNLOCK_PORT(port);
502 return status;
503}
504
505static 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
540static 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
551static 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 */
575MMAL_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
644end:
645 if (connected_port)
646 UNLOCK_CONNECTION(connected_port);
647 UNLOCK_CONNECTION(port);
648
649 return status;
650}
651
652static 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 */
709MMAL_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 */
783MMAL_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. */
830MMAL_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 */
868MMAL_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. */
897void mmal_port_buffer_header_callback(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 */
920void 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. */
935MMAL_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
1009finish:
1010 UNLOCK_CONNECTION(other_port);
1011 UNLOCK_CONNECTION(port);
1012 return status;
1013}
1014
1015/** Disconnect a connected port. */
1016MMAL_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
1078end:
1079 UNLOCK_CONNECTION(other_port);
1080 UNLOCK_CONNECTION(port);
1081 return status;
1082}
1083
1084/** Allocate a payload buffer */
1085uint8_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 */
1124void 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
1150MMAL_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
1191error:
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 */
1199static 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 */
1238static 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 */
1273static 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 */
1283static 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 */
1303static 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 */
1364static 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/*****************************************************************************/
1381static 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
1391static 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 */
1417static 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
1441static 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
1453static 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
1464MMAL_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
1506MMAL_BOOL_T mmal_port_is_connected(MMAL_PORT_T *port)
1507{
1508 return !!port->priv->core->connected_port;
1509}
1510