1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28#include "mmal.h"
29#include "util/mmal_util.h"
30#include "util/mmal_connection.h"
31#include "mmal_logging.h"
32#include <stdio.h>
33
34#define CONNECTION_NAME_FORMAT "%s:%.2222s:%i/%s:%.2222s:%i"
35
36typedef struct
37{
38 MMAL_CONNECTION_T connection; /**< Must be the first member! */
39 MMAL_PORT_T *pool_port; /**< Port used to create the pool */
40
41 /** Reference counting */
42 int refcount;
43
44} MMAL_CONNECTION_PRIVATE_T;
45
46/** Callback from a clock port. Buffer is immediately sent to next component. */
47static void mmal_connection_bh_clock_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
48{
49 MMAL_STATUS_T status = MMAL_SUCCESS;
50 MMAL_CONNECTION_T *connection = (MMAL_CONNECTION_T *)port->userdata;
51 MMAL_PORT_T *other_port = (port == connection->in) ? connection->out : connection->in;
52
53 LOG_TRACE("(%s)%p,%p,%p,%i", port->name, port, buffer, buffer->data, (int)buffer->length);
54
55 if (other_port->is_enabled)
56 {
57 status = mmal_port_send_buffer(other_port, buffer);
58 if (status != MMAL_SUCCESS)
59 {
60 LOG_ERROR("error sending buffer to clock port (%i)", status);
61 mmal_buffer_header_release(buffer);
62 }
63 }
64 else
65 {
66 mmal_buffer_header_release(buffer);
67 }
68}
69
70/** Callback from an input port. Buffer is released. */
71static void mmal_connection_bh_in_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
72{
73 LOG_TRACE("(%s)%p,%p,%p,%i", port->name, port, buffer, buffer->data, (int)buffer->length);
74
75 /* We're done with the buffer, just recycle it */
76 mmal_buffer_header_release(buffer);
77}
78
79/** Callback from an output port. Buffer is queued for the next component. */
80static void mmal_connection_bh_out_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
81{
82 MMAL_CONNECTION_T *connection = (MMAL_CONNECTION_T *)port->userdata;
83
84 LOG_TRACE("(%s)%p,%p,%p,%i", port->name, port, buffer, buffer->data, (int)buffer->length);
85
86 /* Queue the buffer produced by the output port */
87 mmal_queue_put(connection->queue, buffer);
88
89 if (connection->callback)
90 connection->callback(connection);
91}
92
93/** Callback from the pool. Buffer is available. */
94static MMAL_BOOL_T mmal_connection_bh_release_cb(MMAL_POOL_T *pool, MMAL_BUFFER_HEADER_T *buffer,
95 void *userdata)
96{
97 MMAL_CONNECTION_T *connection = (MMAL_CONNECTION_T *)userdata;
98 MMAL_PARAM_UNUSED(pool);
99
100 /* Queue the buffer produced by the output port */
101 mmal_queue_put(pool->queue, buffer);
102
103 if (connection->callback)
104 connection->callback(connection);
105
106 return 0;
107}
108
109/*****************************************************************************/
110static MMAL_STATUS_T mmal_connection_destroy_internal(MMAL_CONNECTION_T *connection)
111{
112 MMAL_STATUS_T status;
113
114 if (connection->is_enabled)
115 {
116 status = mmal_connection_disable(connection);
117 if (status != MMAL_SUCCESS)
118 return status;
119 }
120
121 /* Special case for tunnelling */
122 if (connection->flags & MMAL_CONNECTION_FLAG_TUNNELLING)
123 {
124 status = mmal_port_disconnect(connection->out);
125 if (status != MMAL_SUCCESS)
126 LOG_ERROR("connection %s could not be cleared", connection->name);
127 }
128
129 /* Cleanup resources */
130 if (connection->pool)
131 mmal_pool_destroy(connection->pool);
132 if (connection->queue)
133 mmal_queue_destroy(connection->queue);
134
135 vcos_free(connection);
136 return MMAL_SUCCESS;
137}
138
139/*****************************************************************************/
140MMAL_STATUS_T mmal_connection_destroy(MMAL_CONNECTION_T *connection)
141{
142 MMAL_CONNECTION_PRIVATE_T *private = (MMAL_CONNECTION_PRIVATE_T *)connection;
143
144 LOG_TRACE("%p, %s", connection, connection->name);
145
146 if (--private->refcount)
147 {
148 LOG_DEBUG("delaying destruction of %s (refount %i)", connection->name,
149 private->refcount);
150 return MMAL_SUCCESS;
151 }
152
153 return mmal_connection_destroy_internal(connection);
154}
155
156/*****************************************************************************/
157MMAL_STATUS_T mmal_connection_create(MMAL_CONNECTION_T **cx,
158 MMAL_PORT_T *out, MMAL_PORT_T *in, uint32_t flags)
159{
160 MMAL_STATUS_T status = MMAL_ENOMEM;
161 unsigned int name_size = strlen(out->component->name) + strlen(in->component->name) + sizeof(CONNECTION_NAME_FORMAT);
162 unsigned int size = sizeof(MMAL_CONNECTION_PRIVATE_T) + name_size;
163 MMAL_CONNECTION_PRIVATE_T *private;
164 MMAL_CONNECTION_T *connection;
165 char *name;
166
167 /* Sanity checking */
168 if (!cx)
169 return MMAL_EINVAL;
170
171 private = vcos_malloc(size, "mmal connection");
172 if (!private)
173 return MMAL_ENOMEM;
174 memset(private, 0, size);
175 connection = &private->connection;
176 private->refcount = 1;
177 name = (char *)&private[1];
178
179 vcos_snprintf(name, name_size - 1, CONNECTION_NAME_FORMAT,
180 out->component->name,
181 mmal_port_type_to_string(out->type), (int)out->index,
182 in->component->name,
183 mmal_port_type_to_string(in->type), (int)in->index);
184
185 LOG_TRACE("out %p, in %p, flags %x, %s", out, in, flags, name);
186
187 connection->out = out;
188 connection->in = in;
189 connection->flags = flags;
190 connection->name = name;
191
192 connection->time_setup = vcos_getmicrosecs();
193
194 if (!(connection->flags & MMAL_CONNECTION_FLAG_KEEP_PORT_FORMATS))
195 {
196 /* Set the format of the input port to match the output one */
197 status = mmal_format_full_copy(in->format, out->format);
198 if (status == MMAL_SUCCESS)
199 status = mmal_port_format_commit(in);
200 if (status != MMAL_SUCCESS)
201 {
202 LOG_ERROR("format not set on input port");
203 goto error;
204 }
205 }
206
207 /* In pass-through mode we need to propagate the buffer requirements of the
208 * connected input port */
209 if (out->capabilities & MMAL_PORT_CAPABILITY_PASSTHROUGH)
210 {
211 MMAL_PARAMETER_BUFFER_REQUIREMENTS_T param =
212 {{MMAL_PARAMETER_BUFFER_REQUIREMENTS, sizeof(MMAL_PARAMETER_BUFFER_REQUIREMENTS_T)},
213 in->buffer_num_min, in->buffer_size_min, in->buffer_alignment_min,
214 in->buffer_num_recommended, in->buffer_size_recommended};
215 status = mmal_port_parameter_set(out, &param.hdr);
216 if (status != MMAL_SUCCESS && status != MMAL_ENOSYS)
217 {
218 LOG_ERROR("failed to propagate buffer requirements");
219 goto error;
220 }
221 status = MMAL_SUCCESS;
222 }
223
224 /* Special case for tunnelling */
225 if (connection->flags & MMAL_CONNECTION_FLAG_TUNNELLING)
226 {
227 status = mmal_port_connect(out, in);
228 if (status != MMAL_SUCCESS)
229 LOG_ERROR("connection could not be made");
230 goto done;
231 }
232
233 /* Create empty pool of buffer headers for now (will be resized later on) */
234 private->pool_port = (in->capabilities & MMAL_PORT_CAPABILITY_ALLOCATION) ? in : out;
235 if (flags & MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT)
236 private->pool_port = in;
237 if (flags & MMAL_CONNECTION_FLAG_ALLOCATION_ON_OUTPUT)
238 private->pool_port = out;
239 connection->pool = mmal_port_pool_create(private->pool_port, 0, 0);
240 if (!connection->pool)
241 goto error;
242 mmal_pool_callback_set(connection->pool, mmal_connection_bh_release_cb, (void *)connection);
243
244 /* Create a queue to store the buffers from the output port */
245 connection->queue = mmal_queue_create();
246 if (!connection->queue)
247 goto error;
248
249 done:
250 out->userdata = (void *)connection;
251 in->userdata = (void *)connection;
252 connection->time_setup = vcos_getmicrosecs() - connection->time_setup;
253 *cx = connection;
254 return status;
255
256 error:
257 /* coverity[var_deref_model] mmal_connection_destroy_internal will check connection->pool correctly */
258 mmal_connection_destroy_internal(connection);
259 return status == MMAL_SUCCESS ? MMAL_ENOMEM : status;
260}
261
262/*****************************************************************************/
263void mmal_connection_acquire(MMAL_CONNECTION_T *connection)
264{
265 MMAL_CONNECTION_PRIVATE_T *private = (MMAL_CONNECTION_PRIVATE_T *)connection;
266 LOG_TRACE("connection %s(%p), refcount %i", connection->name, connection,
267 private->refcount);
268 private->refcount++;
269}
270
271/*****************************************************************************/
272MMAL_STATUS_T mmal_connection_release(MMAL_CONNECTION_T *connection)
273{
274 MMAL_CONNECTION_PRIVATE_T *private = (MMAL_CONNECTION_PRIVATE_T *)connection;
275 LOG_TRACE("connection %s(%p), refcount %i", connection->name, connection,
276 private->refcount);
277
278 if (--private->refcount)
279 return MMAL_SUCCESS;
280
281 LOG_TRACE("destroying connection %s(%p)", connection->name, connection);
282 return mmal_connection_destroy_internal(connection);
283}
284
285/*****************************************************************************/
286MMAL_STATUS_T mmal_connection_enable(MMAL_CONNECTION_T *connection)
287{
288 MMAL_PORT_T *in = connection->in, *out = connection->out;
289 uint32_t buffer_num, buffer_size;
290 MMAL_STATUS_T status;
291
292 LOG_TRACE("%p, %s", connection, connection->name);
293
294 if (connection->is_enabled)
295 return MMAL_SUCCESS;
296
297 connection->time_enable = vcos_getmicrosecs();
298
299 /* Override the buffer values with the recommended ones (the port probably knows best) */
300 if (!(connection->flags & MMAL_CONNECTION_FLAG_KEEP_BUFFER_REQUIREMENTS))
301 {
302 if (out->buffer_num_recommended)
303 out->buffer_num = out->buffer_num_recommended;
304 if (out->buffer_size_recommended)
305 out->buffer_size = out->buffer_size_recommended;
306 if (in->buffer_num_recommended)
307 in->buffer_num = in->buffer_num_recommended;
308 if (in->buffer_size_recommended)
309 in->buffer_size = in->buffer_size_recommended;
310 }
311
312 /* Special case for tunnelling */
313 if (connection->flags & MMAL_CONNECTION_FLAG_TUNNELLING)
314 {
315 /* Enable port. No callback because the port is connected. Other end of the connection
316 * will be enabled automatically. */
317 status = mmal_port_enable(out, NULL);
318 if (status)
319 LOG_ERROR("output port couldn't be enabled");
320 goto done;
321 }
322
323 /* Set the buffering properties on both ports */
324 buffer_num = MMAL_MAX(out->buffer_num, in->buffer_num);
325 buffer_size = MMAL_MAX(out->buffer_size, in->buffer_size);
326 out->buffer_num = in->buffer_num = buffer_num;
327 out->buffer_size = in->buffer_size = buffer_size;
328
329 /* In pass-through mode there isn't any need to allocate memory */
330 if (out->capabilities & MMAL_PORT_CAPABILITY_PASSTHROUGH)
331 buffer_size = 0;
332
333 /* Resize the output pool */
334 status = mmal_pool_resize(connection->pool, buffer_num, buffer_size);
335 if (status != MMAL_SUCCESS)
336 {
337 LOG_ERROR("couldn't resize pool");
338 goto done;
339 }
340
341 /* Enable output port. The callback specified here is the function which
342 * will be called when an empty buffer header comes back to the port. */
343 status = mmal_port_enable(out, (out->type == MMAL_PORT_TYPE_CLOCK) ?
344 mmal_connection_bh_clock_cb : mmal_connection_bh_out_cb);
345 if(status)
346 {
347 LOG_ERROR("output port couldn't be enabled");
348 goto done;
349 }
350
351 /* Enable input port. The callback specified here is the function which
352 * will be called when an empty buffer header comes back to the port. */
353 status = mmal_port_enable(in, (in->type == MMAL_PORT_TYPE_CLOCK) ?
354 mmal_connection_bh_clock_cb : mmal_connection_bh_in_cb);
355 if(status)
356 {
357 LOG_ERROR("input port couldn't be enabled");
358 mmal_port_disable(out);
359 goto done;
360 }
361
362 /* Clock ports need buffers to send clock updates, so
363 * populate both connected clock ports */
364 if ((out->type == MMAL_PORT_TYPE_CLOCK) && (in->type == MMAL_PORT_TYPE_CLOCK))
365 {
366 MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(connection->pool->queue);
367 while (buffer)
368 {
369 mmal_port_send_buffer(out, buffer);
370 buffer = mmal_queue_get(connection->pool->queue);
371 if (buffer)
372 {
373 mmal_port_send_buffer(in, buffer);
374 buffer = mmal_queue_get(connection->pool->queue);
375 }
376 }
377 }
378
379 done:
380 connection->time_enable = vcos_getmicrosecs() - connection->time_enable;
381 connection->is_enabled = status == MMAL_SUCCESS;
382 return status;
383}
384
385/*****************************************************************************/
386MMAL_STATUS_T mmal_connection_disable(MMAL_CONNECTION_T *connection)
387{
388 MMAL_STATUS_T status;
389 MMAL_BUFFER_HEADER_T *buffer;
390
391 LOG_TRACE("%p, %s", connection, connection->name);
392
393 if (!connection->is_enabled)
394 return MMAL_SUCCESS;
395
396 connection->time_disable = vcos_getmicrosecs();
397
398 /* Special case for tunnelling */
399 if (connection->flags & MMAL_CONNECTION_FLAG_TUNNELLING)
400 {
401 /* Disable port. Other end of the connection will be disabled automatically. */
402 status = mmal_port_disable(connection->out);
403 if (status)
404 LOG_ERROR("output port couldn't be disabled");
405 goto done;
406 }
407
408 /* Disable input port. */
409 status = mmal_port_disable(connection->in);
410 if(status)
411 {
412 LOG_ERROR("input port couldn't be disabled");
413 goto done;
414 }
415
416 /* Disable output port */
417 status = mmal_port_disable(connection->out);
418 if(status)
419 {
420 LOG_ERROR("output port couldn't be disabled");
421 goto done;
422 }
423
424 /* Flush the queue */
425 buffer = mmal_queue_get(connection->queue);
426 while (buffer)
427 {
428 mmal_buffer_header_release(buffer);
429 buffer = mmal_queue_get(connection->queue);
430 }
431 vcos_assert(mmal_queue_length(connection->pool->queue) == connection->pool->headers_num);
432
433 done:
434 connection->time_disable = vcos_getmicrosecs() - connection->time_disable;
435 connection->is_enabled = !(status == MMAL_SUCCESS);
436 return status;
437}
438
439/*****************************************************************************/
440static MMAL_STATUS_T mmal_connection_reconfigure(MMAL_CONNECTION_T *connection, MMAL_ES_FORMAT_T *format)
441{
442 MMAL_STATUS_T status;
443 LOG_TRACE("%p, %s", connection, connection->name);
444
445 status = mmal_connection_disable(connection);
446 if (status != MMAL_SUCCESS)
447 {
448 LOG_ERROR("connection couldn't be disabled");
449 return status;
450 }
451
452 /* Set the new format for the output port */
453 status = mmal_format_full_copy(connection->out->format, format);
454 if (status == MMAL_SUCCESS)
455 status = mmal_port_format_commit(connection->out);
456 if (status != MMAL_SUCCESS)
457 {
458 LOG_ERROR("commit failed on port %s(%p) (%i)",
459 connection->out->name, connection->out, status);
460 return status;
461 }
462
463 /* Set the new format for the input port */
464 status = mmal_format_full_copy(connection->in->format, connection->out->format);
465 if (status == MMAL_SUCCESS)
466 status = mmal_port_format_commit(connection->in);
467 if (status != MMAL_SUCCESS)
468 {
469 LOG_ERROR("commit failed on port %s(%p) (%i)",
470 connection->in->name, connection->in, status);
471 return status;
472 }
473
474 /* Enable ports */
475 status = mmal_connection_enable(connection);
476 if (status)
477 {
478 LOG_ERROR("connection couldn't be enabled");
479 return status;
480 }
481
482 return MMAL_SUCCESS;
483}
484
485/*****************************************************************************/
486MMAL_STATUS_T mmal_connection_event_format_changed(MMAL_CONNECTION_T *connection,
487 MMAL_BUFFER_HEADER_T *buffer)
488{
489 MMAL_EVENT_FORMAT_CHANGED_T *event;
490 MMAL_STATUS_T status;
491
492 LOG_TRACE("%p, %s", connection, connection->name);
493
494 if (buffer->cmd != MMAL_EVENT_FORMAT_CHANGED)
495 return MMAL_EINVAL;
496
497 event = mmal_event_format_changed_get(buffer);
498 if (!event)
499 return MMAL_EINVAL;
500
501 /* If we don't need to recreate our buffers then we can just forward the event
502 * to the next component (so it gets configured properly) */
503 if ((connection->in->capabilities & MMAL_PORT_CAPABILITY_SUPPORTS_EVENT_FORMAT_CHANGE) &&
504 event->buffer_size_min <= connection->out->buffer_size &&
505 event->buffer_num_min <= connection->out->buffer_num)
506 {
507 status = mmal_format_full_copy(connection->out->format, event->format);
508 if (status == MMAL_SUCCESS)
509 status = mmal_port_format_commit(connection->out);
510 if (status != MMAL_SUCCESS)
511 {
512 LOG_ERROR("format commit failed on port %s(%p) (%i)",
513 connection->out->name, connection->out, status);
514 return status;
515 }
516
517 mmal_buffer_header_acquire(buffer);
518 status = mmal_port_send_buffer(connection->in, buffer);
519 if (status != MMAL_SUCCESS)
520 {
521 LOG_ERROR("buffer send failed on port %s(%p) (%i)",
522 connection->in->name, connection->in, status);
523 mmal_buffer_header_release(buffer);
524 return status;
525 }
526
527 return MMAL_SUCCESS;
528 }
529
530 /* Otherwise we have to reconfigure our pipeline */
531 return mmal_connection_reconfigure(connection, event->format);
532}
533