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 "interface/mmal/mmal_logging.h"
29#include "interface/mmal/mmal.h"
30#include "mmal_vc_api.h"
31#include "mmal_vc_msgs.h"
32#include "mmal_vc_client_priv.h"
33#include "mmal_vc_opaque_alloc.h"
34#include "mmal_vc_shm.h"
35#include "interface/mmal/util/mmal_util.h"
36#include "interface/mmal/core/mmal_component_private.h"
37#include "interface/mmal/core/mmal_port_private.h"
38#include "interface/mmal/core/mmal_buffer_private.h"
39#include "interface/vcos/vcos.h"
40
41/** Private information for MMAL VC components
42 */
43
44typedef enum MMAL_ZEROLEN_CHECK_T
45{
46 ZEROLEN_NOT_INITIALIZED,
47 ZEROLEN_COMPATIBLE,
48 ZEROLEN_INCOMPATIBLE
49} MMAL_ZEROLEN_CHECK_T;
50
51typedef enum MMAL_PORT_FLUSH_CHECK_T
52{
53 PORT_FLUSH_NOT_INITIALIZED,
54 PORT_FLUSH_COMPATIBLE,
55 PORT_FLUSH_INCOMPATIBLE
56} MMAL_PORT_FLUSH_CHECK_T;
57
58typedef struct MMAL_PORT_MODULE_T
59{
60 uint32_t magic;
61 uint32_t component_handle;
62 MMAL_PORT_T *port;
63 uint32_t port_handle;
64
65 MMAL_BOOL_T has_pool;
66 VCOS_BLOCKPOOL_T pool;
67
68 MMAL_BOOL_T is_zero_copy;
69 MMAL_BOOL_T zero_copy_workaround;
70 uint32_t opaque_allocs;
71
72 MMAL_BOOL_T sent_data_on_port;
73
74 MMAL_PORT_T *connected; /**< Connected port if any */
75} MMAL_PORT_MODULE_T;
76
77typedef struct MMAL_COMPONENT_MODULE_T
78{
79 uint32_t component_handle;
80
81 MMAL_PORT_MODULE_T **ports;
82 uint32_t ports_num;
83
84 MMAL_QUEUE_T *callback_queue; /**< Used to queue the callbacks we need to make to the client */
85
86 MMAL_BOOL_T event_ctx_initialised;
87 MMAL_VC_CLIENT_BUFFER_CONTEXT_T event_ctx; /**< Used as the ctx for event buffers */
88 uint32_t event_ctx_handle; /**< Used as the ctx for event buffers */
89} MMAL_COMPONENT_MODULE_T;
90
91
92/*****************************************************************************
93 * Local function prototypes
94 *****************************************************************************/
95static void mmal_vc_do_callback(MMAL_COMPONENT_T *component);
96static MMAL_STATUS_T mmal_vc_port_info_get(MMAL_PORT_T *port);
97
98/*****************************************************************************/
99MMAL_STATUS_T mmal_vc_get_version(uint32_t *major, uint32_t *minor, uint32_t *minimum)
100{
101 mmal_worker_version msg;
102 size_t len = sizeof(msg);
103 MMAL_STATUS_T status;
104
105 status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg),
106 MMAL_WORKER_GET_VERSION, &msg, &len, MMAL_FALSE);
107
108 if (status != MMAL_SUCCESS)
109 return status;
110
111 if (!vcos_verify(len == sizeof(msg)))
112 return MMAL_EINVAL;
113
114 *major = msg.major;
115 *minor = msg.minor;
116 *minimum = msg.minimum;
117 return MMAL_SUCCESS;
118}
119
120/*****************************************************************************/
121MMAL_STATUS_T mmal_vc_get_stats(MMAL_VC_STATS_T *stats, int reset)
122{
123 mmal_worker_stats msg;
124 size_t len = sizeof(msg);
125 msg.reset = reset;
126
127 MMAL_STATUS_T status = mmal_vc_sendwait_message(mmal_vc_get_client(),
128 &msg.header, sizeof(msg),
129 MMAL_WORKER_GET_STATS,
130 &msg, &len, MMAL_FALSE);
131
132
133 if (status == MMAL_SUCCESS)
134 {
135 vcos_assert(len == sizeof(msg));
136 *stats = msg.stats;
137 }
138 return status;
139}
140
141/** Set port buffer requirements. */
142static MMAL_STATUS_T mmal_vc_port_requirements_set(MMAL_PORT_T *port)
143{
144 MMAL_PORT_MODULE_T *module = port->priv->module;
145 MMAL_STATUS_T status;
146 mmal_worker_reply reply;
147 mmal_worker_port_action msg;
148 size_t replylen = sizeof(reply);
149
150 msg.component_handle = module->component_handle;
151 msg.action = MMAL_WORKER_PORT_ACTION_SET_REQUIREMENTS;
152 msg.port_handle = module->port_handle;
153 msg.param.enable.port.buffer_num = port->buffer_num;
154 msg.param.enable.port.buffer_size = port->buffer_size;
155
156 status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg),
157 MMAL_WORKER_PORT_ACTION, &reply, &replylen, MMAL_FALSE);
158 if (status == MMAL_SUCCESS)
159 {
160 vcos_assert(replylen == sizeof(reply));
161 status = reply.status;
162 }
163 if (status != MMAL_SUCCESS)
164 LOG_ERROR("failed to set port requirements (%i/%i,%i/%i)",
165 port->buffer_num, port->buffer_num_min,
166 port->buffer_size, port->buffer_size_min);
167
168 return status;
169}
170
171/** Get port buffer requirements. */
172static MMAL_STATUS_T mmal_vc_port_requirements_get(MMAL_PORT_T *port)
173{
174 MMAL_PORT_MODULE_T *module = port->priv->module;
175 mmal_worker_port_info_get msg;
176 mmal_worker_port_info reply;
177 size_t replylen = sizeof(reply);
178 MMAL_STATUS_T status;
179
180 msg.component_handle = module->component_handle;
181 msg.port_type = port->type;
182 msg.index = port->index;
183
184 LOG_TRACE("get port requirements (%i:%i)", port->type, port->index);
185
186 status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg),
187 MMAL_WORKER_PORT_INFO_GET, &reply, &replylen, MMAL_FALSE);
188 if (status == MMAL_SUCCESS)
189 {
190 vcos_assert(replylen == sizeof(reply));
191 status = reply.status;
192 }
193 if (status != MMAL_SUCCESS)
194 {
195 LOG_ERROR("failed to get port requirements (%i:%i)", port->type, port->index);
196 return status;
197 }
198
199 port->buffer_num_min = reply.port.buffer_num_min;
200 port->buffer_num_recommended = reply.port.buffer_num_recommended;
201 port->buffer_size_min = reply.port.buffer_size_min;
202 port->buffer_size_recommended = reply.port.buffer_size_recommended;
203 port->buffer_alignment_min = reply.port.buffer_alignment_min;
204
205 return MMAL_SUCCESS;
206}
207
208/** Enable processing on a port */
209static MMAL_STATUS_T mmal_vc_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb)
210{
211 MMAL_PORT_MODULE_T *module = port->priv->module;
212 MMAL_STATUS_T status;
213 mmal_worker_reply reply;
214 mmal_worker_port_action msg;
215 size_t replylen = sizeof(reply);
216 MMAL_PARAM_UNUSED(cb);
217
218 if (!port->component->priv->module->event_ctx_initialised)
219 {
220 MMAL_POOL_T *pool = port->component->priv->event_pool;
221 MMAL_DRIVER_BUFFER_T *drv;
222 unsigned int i;
223
224 /* We need to associate our vc client context to all our event buffers.
225 * This only needs to be done when the first port is enabled because no event
226 * can be received on disabled ports. */
227 for (i = 0; i < pool->headers_num; i++)
228 {
229 drv = mmal_buffer_header_driver_data(pool->header[i]);
230 drv->client_context = port->component->priv->module->event_ctx_handle;
231 drv->magic = MMAL_MAGIC;
232 }
233
234 port->component->priv->module->event_ctx_initialised = MMAL_TRUE;
235 }
236
237 if (!module->connected)
238 {
239 if (vcos_blockpool_create_on_heap(&module->pool, port->buffer_num,
240 sizeof(MMAL_VC_CLIENT_BUFFER_CONTEXT_T),
241 VCOS_BLOCKPOOL_ALIGN_DEFAULT, VCOS_BLOCKPOOL_FLAG_NONE, "mmal vc port pool") != VCOS_SUCCESS)
242 {
243 LOG_ERROR("failed to create port pool");
244 return MMAL_ENOMEM;
245 }
246 module->has_pool = 1;
247 }
248
249 if (module->connected)
250 {
251 /* The connected port won't be enabled explicitly so make sure we apply
252 * the buffer requirements now. */
253 status = mmal_vc_port_requirements_set(module->connected);
254 if (status != MMAL_SUCCESS)
255 goto error;
256 }
257
258 msg.component_handle = module->component_handle;
259 msg.action = MMAL_WORKER_PORT_ACTION_ENABLE;
260 msg.port_handle = module->port_handle;
261 msg.param.enable.port.buffer_num = port->buffer_num;
262 msg.param.enable.port.buffer_size = port->buffer_size;
263
264 status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg),
265 MMAL_WORKER_PORT_ACTION, &reply, &replylen, MMAL_FALSE);
266 if (status == MMAL_SUCCESS)
267 {
268 vcos_assert(replylen == sizeof(reply));
269 status = reply.status;
270 }
271 if (status != MMAL_SUCCESS)
272 {
273 LOG_ERROR("failed to enable port %s: %s",
274 port->name, mmal_status_to_string(status));
275 goto error;
276 }
277
278 if (module->connected)
279 mmal_vc_port_info_get(module->connected);
280
281 return MMAL_SUCCESS;
282
283 error:
284 if (module->has_pool)
285 vcos_blockpool_delete(&module->pool);
286 return status;
287}
288
289/** Disable processing on a port */
290static MMAL_STATUS_T mmal_vc_port_disable(MMAL_PORT_T *port)
291{
292 MMAL_PORT_MODULE_T *module = port->priv->module;
293 MMAL_STATUS_T status;
294 mmal_worker_reply reply;
295 mmal_worker_port_action msg;
296 size_t replylen = sizeof(reply);
297
298 msg.component_handle = module->component_handle;
299 msg.action = MMAL_WORKER_PORT_ACTION_DISABLE;
300 msg.port_handle = module->port_handle;
301
302 status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg),
303 MMAL_WORKER_PORT_ACTION, &reply, &replylen, MMAL_FALSE);
304 if (status == MMAL_SUCCESS)
305 {
306 vcos_assert(replylen == sizeof(reply));
307 status = reply.status;
308 }
309 if (status != MMAL_SUCCESS)
310 LOG_ERROR("failed to disable port - reason %d", status);
311
312 if (module->has_pool)
313 {
314 /* MMAL server should make sure that all buffers are sent back before it
315 * disables the port. */
316 vcos_assert(vcos_blockpool_available_count(&module->pool) == port->buffer_num);
317 vcos_blockpool_delete(&module->pool);
318 module->has_pool = 0;
319 }
320
321 /* We need to make sure all the queued callbacks have been done */
322 while (mmal_queue_length(port->component->priv->module->callback_queue))
323 mmal_vc_do_callback(port->component);
324
325 if (module->connected)
326 mmal_vc_port_info_get(module->connected);
327
328 return status;
329}
330
331/** Flush a port using MMAL_WORKER_PORT_ACTION - when the port is zero-copy or no data has been sent */
332static MMAL_STATUS_T mmal_vc_port_flush_normal(MMAL_PORT_T *port)
333{
334 MMAL_PORT_MODULE_T *module = port->priv->module;
335 MMAL_STATUS_T status;
336 mmal_worker_reply reply;
337 mmal_worker_port_action msg;
338 size_t replylen = sizeof(reply);
339
340 msg.component_handle = module->component_handle;
341 msg.action = MMAL_WORKER_PORT_ACTION_FLUSH;
342 msg.port_handle = module->port_handle;
343
344 status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg),
345 MMAL_WORKER_PORT_ACTION, &reply, &replylen, MMAL_FALSE);
346 if (status == MMAL_SUCCESS)
347 {
348 vcos_assert(replylen == sizeof(reply));
349 status = reply.status;
350 }
351 if (status != MMAL_SUCCESS)
352 LOG_ERROR("failed to disable port - reason %d", status);
353
354 return status;
355}
356
357
358/** Flush a port using PORT_FLUSH - generates a dummy bulk transfer to keep it in sync
359 * with buffers being passed using bulk transfer */
360static MMAL_STATUS_T mmal_vc_port_flush_sync(MMAL_PORT_T *port)
361{
362 MMAL_PORT_MODULE_T *module = port->priv->module;
363 MMAL_STATUS_T status;
364 mmal_worker_reply reply;
365 MMAL_VC_CLIENT_BUFFER_CONTEXT_T client_context;
366 mmal_worker_buffer_from_host *msg;
367
368 size_t replylen = sizeof(reply);
369
370 msg = &client_context.msg;
371
372 client_context.magic = MMAL_MAGIC;
373 client_context.port = port;
374
375 msg->drvbuf.client_context = mmal_vc_allocate_client_context(&client_context);
376 msg->drvbuf.component_handle = module->component_handle;
377 msg->drvbuf.port_handle = module->port_handle;
378 msg->drvbuf.magic = MMAL_MAGIC;
379
380
381 status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg->header, sizeof(*msg),
382 MMAL_WORKER_PORT_FLUSH, &reply, &replylen, MMAL_TRUE);
383 if (status == MMAL_SUCCESS)
384 {
385 vcos_assert(replylen == sizeof(reply));
386 status = reply.status;
387 }
388 if (status != MMAL_SUCCESS)
389 LOG_ERROR("failed to disable port - reason %d", status);
390
391 mmal_vc_release_client_context(&client_context);
392 return status;
393}
394
395/** Flush a port */
396static MMAL_STATUS_T mmal_vc_port_flush(MMAL_PORT_T *port)
397{
398 static MMAL_PORT_FLUSH_CHECK_T is_port_flush_compatible = PORT_FLUSH_NOT_INITIALIZED;
399 uint32_t major = 0, minor = 0, minimum = 0;
400 MMAL_STATUS_T status;
401 /* Buffers sent to videocore, if not zero-copy, use vchiq bulk transfers to copy the data.
402 A flush could be sent while one of these buffers is being copied. If the normal flushing method
403 is used, the flush can arrive before the buffer, which causes confusion when a pre-flush buffer
404 arrives after the flush. So use a special flush mode that uses a dummy vchiq transfer to synchronise
405 things.
406 If data has never been sent on the port, then we don't need to worry about a flush overtaking data.
407 In that case, the port may not actually be set up on the other end to receive bulk transfers, so use
408 the normal flushing mechanism in that case.
409 */
410
411 if (port->priv->module->is_zero_copy || !port->priv->module->sent_data_on_port)
412 return mmal_vc_port_flush_normal(port);
413
414 if (is_port_flush_compatible == PORT_FLUSH_NOT_INITIALIZED)
415 {
416 status = mmal_vc_get_version(&major, &minor, &minimum);
417 if (major >= 15)
418 {
419 is_port_flush_compatible = PORT_FLUSH_COMPATIBLE;
420 }
421 else
422 {
423 LOG_ERROR("Version number of MMAL Server incompatible. Required Major:14 Minor: 2 \
424 or Greater. Current Major %d , Minor %d",major,minor);
425 is_port_flush_compatible = PORT_FLUSH_INCOMPATIBLE;
426 }
427 }
428
429 if (is_port_flush_compatible == PORT_FLUSH_COMPATIBLE)
430 return mmal_vc_port_flush_sync(port);
431 else
432 return mmal_vc_port_flush_normal(port);
433}
434
435
436/** Connect 2 ports together */
437static MMAL_STATUS_T mmal_vc_port_connect(MMAL_PORT_T *port, MMAL_PORT_T *other_port)
438{
439 MMAL_PORT_MODULE_T *module = port->priv->module;
440 MMAL_STATUS_T status;
441 mmal_worker_reply reply;
442 mmal_worker_port_action msg;
443 size_t replylen = sizeof(reply);
444
445 /* We only support connecting vc components together */
446 if (other_port && port->priv->pf_enable != other_port->priv->pf_enable)
447 return MMAL_ENOSYS;
448
449 /* Send the request to the video side */
450 msg.component_handle = module->component_handle;
451 msg.action = other_port ? MMAL_WORKER_PORT_ACTION_CONNECT : MMAL_WORKER_PORT_ACTION_DISCONNECT;
452 msg.port_handle = module->port_handle;
453 if (other_port)
454 {
455 msg.param.connect.component_handle = other_port->priv->module->component_handle;
456 msg.param.connect.port_handle = other_port->priv->module->port_handle;
457 }
458
459 status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg),
460 MMAL_WORKER_PORT_ACTION, &reply, &replylen, MMAL_FALSE);
461 if (status == MMAL_SUCCESS)
462 {
463 vcos_assert(replylen == sizeof(reply));
464 status = reply.status;
465 }
466
467 if (status != MMAL_SUCCESS)
468 {
469 LOG_ERROR("failed to connect ports: %s", mmal_status_to_string(status));
470 return status;
471 }
472
473 if (other_port)
474 {
475 /* Connection */
476 module->connected = other_port;
477 other_port->priv->module->connected = port;
478 }
479 else
480 {
481 /* Disconnection */
482 if (module->connected)
483 module->connected->priv->module->connected = NULL;
484 module->connected = NULL;
485 }
486
487 return MMAL_SUCCESS;
488}
489
490/*****************************************************************************/
491static void mmal_vc_do_callback(MMAL_COMPONENT_T *component)
492{
493 MMAL_COMPONENT_MODULE_T *module = component->priv->module;
494 MMAL_BUFFER_HEADER_T *buffer;
495 MMAL_PORT_T *port;
496
497 /* Get a buffer from this port */
498 buffer = mmal_queue_get(module->callback_queue);
499 if (!buffer)
500 return; /* Will happen when a port gets disabled */
501
502 port = (MMAL_PORT_T *)buffer->priv->component_data;
503
504 /* Catch and report any transmission error */
505 if (buffer->flags & MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED)
506 mmal_event_error_send(port->component, MMAL_EIO);
507
508 /* Events generated by this component are handled differently */
509 if (mmal_buffer_header_driver_data(buffer)->client_context ==
510 component->priv->module->event_ctx_handle)
511 {
512 mmal_port_event_send(port, buffer);
513 return;
514 }
515
516 buffer->data = mmal_vc_shm_lock(buffer->data, port->priv->module->zero_copy_workaround);
517 mmal_port_buffer_header_callback(port, buffer);
518}
519
520static void mmal_vc_do_callback_loop(MMAL_COMPONENT_T *component)
521{
522 while (mmal_queue_length(component->priv->module->callback_queue))
523 mmal_vc_do_callback(component);
524}
525
526/** Called back from VCHI(Q) event handler when buffers come back from the copro.
527 *
528 * The message points to the message sent by videocore, and which should have
529 * a pointer back to our original client side context.
530 *
531 */
532static void mmal_vc_port_send_callback(mmal_worker_buffer_from_host *msg)
533{
534 MMAL_BUFFER_HEADER_T *buffer;
535 MMAL_PORT_T *port;
536 MMAL_VC_CLIENT_BUFFER_CONTEXT_T *client_context = mmal_vc_lookup_client_context(msg->drvbuf.client_context);
537
538 vcos_assert(client_context);
539 vcos_assert(client_context->magic == MMAL_MAGIC);
540
541 buffer = client_context->buffer;
542 port = client_context->port;
543
544 vcos_blockpool_free(client_context);
545 mmal_vc_release_client_context(client_context);
546
547 vcos_assert(port->priv->module->magic == MMAL_MAGIC);
548 mmal_vc_msg_to_buffer_header(buffer, msg);
549
550 /* Queue the callback so it is delivered by the action thread */
551 buffer->priv->component_data = (void *)port;
552 mmal_queue_put(port->component->priv->module->callback_queue, buffer);
553 mmal_component_action_trigger(port->component);
554}
555
556static void mmal_vc_port_send_event_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
557{
558 /* Queue the event to be delivered by the action thread */
559 buffer->priv->component_data = (void *)port;
560 mmal_queue_put(port->component->priv->module->callback_queue, buffer);
561 mmal_component_action_trigger(port->component);
562}
563
564/** Called from the client to send a buffer (empty or full) to
565 * the copro.
566 */
567static MMAL_STATUS_T mmal_vc_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
568{
569 MMAL_PORT_MODULE_T *module = port->priv->module;
570 MMAL_STATUS_T status;
571 MMAL_VC_CLIENT_BUFFER_CONTEXT_T *client_context;
572 mmal_worker_buffer_from_host *msg;
573 uint32_t length;
574 uint32_t msgid = MMAL_WORKER_BUFFER_FROM_HOST;
575 uint32_t major = 0, minor = 0, minimum = 0;
576 static MMAL_ZEROLEN_CHECK_T is_vc_zerolength_compatible = ZEROLEN_NOT_INITIALIZED;
577
578 vcos_assert(port);
579 vcos_assert(module);
580 vcos_assert(module->magic == MMAL_MAGIC);
581
582 /* Handle event buffers */
583 if (buffer->cmd)
584 {
585 MMAL_EVENT_FORMAT_CHANGED_T *event = mmal_event_format_changed_get(buffer);
586 if (event)
587 {
588 mmal_format_copy(port->format, event->format);
589 status = port->priv->pf_set_format(port);
590 if(status != MMAL_SUCCESS)
591 LOG_ERROR("format not set on port %p", port);
592 }
593 else
594 {
595 LOG_ERROR("discarding event %i on port %p", (int)buffer->cmd, port);
596 }
597
598 buffer->length = 0;
599 mmal_port_buffer_header_callback(port, buffer);
600 return MMAL_SUCCESS;
601 }
602
603 /* We can only send buffers if we have a pool */
604 if (!module->has_pool)
605 {
606 LOG_ERROR("no pool on port %p", port);
607 return MMAL_EINVAL;
608 }
609
610 client_context = vcos_blockpool_alloc(&module->pool);
611 if(!client_context)
612 {
613 LOG_INFO("couldn't allocate client buffer context from pool");
614 return MMAL_ENOMEM;
615 }
616 msg = &client_context->msg;
617
618 client_context->magic = MMAL_MAGIC;
619 client_context->buffer = buffer;
620 client_context->callback = mmal_vc_port_send_callback;
621 client_context->callback_event = NULL;
622 client_context->port = port;
623
624 msg->drvbuf.client_context = mmal_vc_allocate_client_context(client_context);
625 msg->drvbuf.component_handle = module->component_handle;
626 msg->drvbuf.port_handle = module->port_handle;
627 msg->drvbuf.magic = MMAL_MAGIC;
628
629 length = buffer->length;
630
631 if (length <= MMAL_VC_SHORT_DATA && !port->priv->module->is_zero_copy &&
632 (port->format->encoding == MMAL_ENCODING_OPAQUE ||
633 port->type == MMAL_PORT_TYPE_CLOCK))
634 {
635 memcpy(msg->short_data, buffer->data + buffer->offset, buffer->length);
636 msg->payload_in_message = length;
637 length = 0;
638 }
639 else
640 {
641 msg->payload_in_message = 0;
642 }
643
644 buffer->data =
645 mmal_vc_shm_unlock(buffer->data, &length, port->priv->module->zero_copy_workaround);
646 mmal_vc_buffer_header_to_msg(msg, buffer);
647
648 if (!VCOS_BLOCKPOOL_IS_VALID_HANDLE_FORMAT(msg->drvbuf.component_handle, 256))
649 {
650 LOG_ERROR("bad component handle 0x%x", msg->drvbuf.component_handle);
651 mmal_vc_release_client_context(client_context);
652 return MMAL_EINVAL;
653 }
654
655 if (msg->drvbuf.port_handle > 255)
656 {
657 LOG_ERROR("bad port handle 0x%x", msg->drvbuf.port_handle);
658 mmal_vc_release_client_context(client_context);
659 return MMAL_EINVAL;
660 }
661
662 if (module->is_zero_copy)
663 length = 0;
664
665 if (is_vc_zerolength_compatible == ZEROLEN_NOT_INITIALIZED)
666 {
667 status = mmal_vc_get_version(&major, &minor, &minimum);
668 if ((major > 12 ) || ((major == 12) && (minor >= 2)))
669 {
670 is_vc_zerolength_compatible = ZEROLEN_COMPATIBLE;
671 }
672 else
673 {
674 LOG_ERROR("Version number of MMAL Server incompatible. Required Major:12 Minor: 2 \
675 or Greater. Current Major %d , Minor %d",major,minor);
676 is_vc_zerolength_compatible = ZEROLEN_INCOMPATIBLE;
677 }
678 }
679
680 if ((is_vc_zerolength_compatible == ZEROLEN_COMPATIBLE) && !(module->is_zero_copy) && !length
681 && (msg->buffer_header.flags & MMAL_BUFFER_HEADER_FLAG_EOS))
682 {
683 length = 8;
684 msgid = MMAL_WORKER_BUFFER_FROM_HOST_ZEROLEN;
685 }
686
687 if (length)
688 {
689 // We're doing a bulk transfer. Note this so that flushes know
690 // they need to use the more cumbersome fake-bulk-transfer mechanism
691 // to guarantee correct ordering.
692 port->priv->module->sent_data_on_port = MMAL_TRUE;
693
694 // Data will be received at the start of the destination buffer, so fixup
695 // the offset in the destination buffer header.
696 msg->buffer_header.offset = 0;
697 }
698
699 status = mmal_vc_send_message(mmal_vc_get_client(), &msg->header, sizeof(*msg),
700 buffer->data + buffer->offset, length,
701 msgid);
702 if (status != MMAL_SUCCESS)
703 {
704 LOG_INFO("failed %d", status);
705 vcos_blockpool_free(client_context);
706 mmal_vc_release_client_context(client_context);
707 buffer->data = mmal_vc_shm_lock(buffer->data, port->priv->module->zero_copy_workaround);
708 }
709
710 return status;
711}
712
713static MMAL_STATUS_T mmal_vc_component_disable(MMAL_COMPONENT_T *component)
714{
715 MMAL_STATUS_T status;
716 mmal_worker_reply reply;
717 mmal_worker_component_disable msg;
718 size_t replylen = sizeof(reply);
719
720 vcos_assert(component && component->priv && component->priv->module);
721
722 msg.component_handle = component->priv->module->component_handle;
723
724 status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg),
725 MMAL_WORKER_COMPONENT_DISABLE,
726 &reply, &replylen, MMAL_FALSE);
727
728 if (status == MMAL_SUCCESS)
729 {
730 vcos_assert(replylen == sizeof(reply));
731 status = reply.status;
732 }
733
734 if (status != MMAL_SUCCESS && status != MMAL_ENOSYS)
735 {
736 LOG_ERROR("failed to disable component - reason %d", status);
737 goto fail;
738 }
739
740 return status;
741fail:
742 return status;
743}
744
745static MMAL_STATUS_T mmal_vc_component_enable(MMAL_COMPONENT_T *component)
746{
747 MMAL_STATUS_T status;
748 mmal_worker_reply reply;
749 mmal_worker_component_enable msg;
750 size_t replylen = sizeof(reply);
751
752 vcos_assert(component && component->priv && component->priv->module);
753
754 msg.component_handle = component->priv->module->component_handle;
755
756 status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg),
757 MMAL_WORKER_COMPONENT_ENABLE, &reply, &replylen, MMAL_FALSE);
758
759 if (status == MMAL_SUCCESS)
760 {
761 vcos_assert(replylen == sizeof(reply));
762 status = reply.status;
763 }
764
765 if (status != MMAL_SUCCESS && status != MMAL_ENOSYS)
766 {
767 LOG_ERROR("failed to enable component: %s", mmal_status_to_string(status));
768 return status;
769 }
770
771 return MMAL_SUCCESS;
772}
773
774static MMAL_STATUS_T mmal_vc_component_destroy(MMAL_COMPONENT_T *component)
775{
776 MMAL_STATUS_T status;
777 mmal_worker_component_destroy msg;
778 mmal_worker_reply reply;
779 size_t replylen = sizeof(reply);
780
781 vcos_assert(component && component->priv && component->priv->module);
782
783 msg.component_handle = component->priv->module->component_handle;
784
785 status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg),
786 MMAL_WORKER_COMPONENT_DESTROY,
787 &reply, &replylen, MMAL_FALSE);
788
789 if (status == MMAL_SUCCESS)
790 {
791 vcos_assert(replylen == sizeof(reply));
792 status = reply.status;
793 }
794 if (status != MMAL_SUCCESS)
795 {
796 LOG_ERROR("failed to destroy component - reason %d", status );
797 goto fail;
798 }
799
800 if(component->input_num)
801 mmal_ports_free(component->input, component->input_num);
802 if(component->output_num)
803 mmal_ports_free(component->output, component->output_num);
804 if(component->clock_num)
805 mmal_ports_free(component->clock, component->clock_num);
806
807 mmal_queue_destroy(component->priv->module->callback_queue);
808 mmal_vc_release_client_context(&component->priv->module->event_ctx);
809
810 vcos_free(component->priv->module);
811 component->priv->module = NULL;
812
813fail:
814 // no longer require videocore
815 mmal_vc_release();
816 mmal_vc_release_client_component(component);
817 mmal_vc_shm_exit();
818 mmal_vc_deinit();
819 return status;
820}
821
822MMAL_STATUS_T mmal_vc_consume_mem(size_t size, uint32_t *handle)
823{
824 MMAL_STATUS_T status;
825 mmal_worker_consume_mem req;
826 mmal_worker_consume_mem reply;
827 size_t len = sizeof(reply);
828
829 req.size = (uint32_t) size;
830
831 status = mmal_vc_sendwait_message(mmal_vc_get_client(),
832 &req.header, sizeof(req),
833 MMAL_WORKER_CONSUME_MEM,
834 &reply, &len, MMAL_FALSE);
835 if (status == MMAL_SUCCESS)
836 {
837 vcos_assert(len == sizeof(reply));
838 status = reply.status;
839 *handle = reply.handle;
840 }
841 return status;
842}
843
844MMAL_STATUS_T mmal_vc_compact(MMAL_VC_COMPACT_MODE_T mode, uint32_t *duration)
845{
846 MMAL_STATUS_T status;
847 mmal_worker_compact req;
848 mmal_worker_compact reply;
849 size_t len = sizeof(reply);
850
851 req.mode = (uint32_t)mode;
852 status = mmal_vc_sendwait_message(mmal_vc_get_client(),
853 &req.header, sizeof(req),
854 MMAL_WORKER_COMPACT,
855 &reply, &len, MMAL_FALSE);
856 if (status == MMAL_SUCCESS)
857 {
858 vcos_assert(len == sizeof(reply));
859 status = reply.status;
860 *duration = reply.duration;
861 }
862 return status;
863}
864
865MMAL_STATUS_T mmal_vc_lmk(uint32_t alloc_size)
866{
867 MMAL_STATUS_T status;
868 mmal_worker_lmk req;
869 mmal_worker_lmk reply;
870 size_t len = sizeof(reply);
871
872 req.alloc_size = alloc_size;
873
874 status = mmal_vc_sendwait_message(mmal_vc_get_client(),
875 &req.header, sizeof(req),
876 MMAL_WORKER_LMK,
877 &reply, &len, MMAL_FALSE);
878 return status;
879}
880
881MMAL_STATUS_T mmal_vc_host_log(const char *msg)
882{
883 MMAL_STATUS_T status = MMAL_EINVAL;
884 if (msg)
885 {
886 mmal_worker_host_log req;
887 mmal_worker_reply reply;
888 size_t replylen = sizeof(reply);
889 size_t msg_len = vcos_safe_strcpy(req.msg, msg, sizeof(req.msg), 0);
890
891 /* Reduce the length if it is shorter than the max message length */
892 status = mmal_vc_sendwait_message(mmal_vc_get_client(), &req.header,
893 sizeof(req) - sizeof(req.msg) + vcos_min(sizeof(req.msg), msg_len + 1),
894 MMAL_WORKER_HOST_LOG,
895 &reply, &replylen, MMAL_FALSE);
896
897 if (status == MMAL_SUCCESS)
898 {
899 vcos_assert(replylen == sizeof(reply));
900 status = reply.status;
901 }
902 }
903 return status;
904}
905
906MMAL_STATUS_T mmal_vc_get_core_stats(MMAL_CORE_STATISTICS_T *stats,
907 MMAL_STATS_RESULT_T *result,
908 char *name,
909 size_t namelen,
910 MMAL_PORT_TYPE_T type,
911 unsigned component_index,
912 unsigned port_index,
913 MMAL_CORE_STATS_DIR dir,
914 MMAL_BOOL_T reset)
915{
916 mmal_worker_get_core_stats_for_port req;
917 mmal_worker_get_core_stats_for_port_reply reply;
918 MMAL_STATUS_T status;
919 size_t len = sizeof(reply);
920
921 req.component_index = component_index;
922 req.port_index = port_index;
923 req.type = type;
924 req.reset = reset;
925 req.dir = dir;
926
927 status = mmal_vc_sendwait_message(mmal_vc_get_client(),
928 &req.header, sizeof(req),
929 MMAL_WORKER_GET_CORE_STATS_FOR_PORT,
930 &reply, &len, MMAL_FALSE);
931
932 if (status == MMAL_SUCCESS)
933 {
934 vcos_assert(len == sizeof(reply));
935 *stats = reply.stats;
936 *result = reply.result;
937 strncpy(name, reply.component_name, namelen);
938 name[namelen-1] = '\0';
939 }
940 return status;
941}
942
943static void mmal_vc_copy_es_format_to_vc(MMAL_ES_FORMAT_T *src, MMAL_VC_ES_FORMAT_T *dest)
944{
945 // IPC MMAL_VC_ES_FORMAT_T is not necessarily the same as MMAL_ES_FORMAT_T,
946 // so copy fields individually.
947 dest->type = src->type;
948 dest->encoding = src->encoding;
949 dest->encoding_variant = src->encoding_variant;
950 dest->bitrate = src->bitrate;
951 dest->flags = src->flags;
952 dest->extradata_size = src->extradata_size;
953}
954
955/** Get port context data. */
956static MMAL_STATUS_T mmal_vc_port_info_get(MMAL_PORT_T *port)
957{
958 MMAL_PORT_MODULE_T *module = port->priv->module;
959 mmal_worker_port_info_get msg;
960 mmal_worker_port_info reply;
961 size_t replylen = sizeof(reply);
962 MMAL_STATUS_T status;
963
964 msg.component_handle = module->component_handle;
965 msg.port_type = port->type;
966 msg.index = port->index;
967
968 LOG_TRACE("get port info (%i:%i)", port->type, port->index);
969
970 status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg),
971 MMAL_WORKER_PORT_INFO_GET, &reply, &replylen, MMAL_FALSE);
972 if (status == MMAL_SUCCESS)
973 {
974 vcos_assert(replylen == sizeof(reply));
975 status = reply.status;
976 }
977
978 if (status != MMAL_SUCCESS)
979 {
980 LOG_ERROR("failed to get port info (%i:%i): %s", port->type, port->index,
981 mmal_status_to_string(status));
982 return status;
983 }
984
985 module->port_handle = reply.port_handle;
986 port->buffer_num_min = reply.port.buffer_num_min;
987 port->buffer_num_recommended = reply.port.buffer_num_recommended;
988 port->buffer_num = reply.port.buffer_num;
989 port->buffer_size_min = reply.port.buffer_size_min;
990 port->buffer_size_recommended = reply.port.buffer_size_recommended;
991 port->buffer_size = reply.port.buffer_size;
992 port->buffer_alignment_min = reply.port.buffer_alignment_min;
993 port->is_enabled = reply.port.is_enabled;
994 port->capabilities = reply.port.capabilities;
995
996 mmal_vc_copy_es_format_from_vc(&reply.format, port->format);
997
998 *port->format->es = reply.es;
999 if(port->format->extradata_size)
1000 {
1001 status = mmal_format_extradata_alloc(port->format, port->format->extradata_size);
1002 if(status != MMAL_SUCCESS)
1003 {
1004 vcos_assert(0);
1005 port->format->extradata_size = 0;
1006 LOG_ERROR("couldn't allocate extradata %i", port->format->extradata_size);
1007 return MMAL_ENOMEM;
1008 }
1009 memcpy(port->format->extradata, reply.extradata, port->format->extradata_size);
1010 }
1011
1012 return MMAL_SUCCESS;
1013}
1014
1015/** Set port context data. */
1016static MMAL_STATUS_T mmal_vc_port_info_set(MMAL_PORT_T *port)
1017{
1018 MMAL_PORT_MODULE_T *module = port->priv->module;
1019 mmal_worker_port_info_set msg;
1020 mmal_worker_port_info reply;
1021 size_t replylen = sizeof(reply);
1022 MMAL_STATUS_T status;
1023
1024 msg.component_handle = module->component_handle;
1025 msg.port_type = port->type;
1026 msg.index = port->index;
1027
1028 //Only copy the values that are used into the MMAL_PORT_T of the IPC.
1029 msg.port.buffer_num = port->buffer_num;
1030 msg.port.buffer_size = port->buffer_size;
1031 msg.port.is_enabled = port->is_enabled;
1032
1033 mmal_vc_copy_es_format_to_vc(port->format, &msg.format);
1034
1035 msg.es = *port->format->es;
1036
1037 if(msg.format.extradata_size > MMAL_FORMAT_EXTRADATA_MAX_SIZE)
1038 {
1039 vcos_assert(0);
1040 msg.format.extradata_size = MMAL_FORMAT_EXTRADATA_MAX_SIZE;
1041 }
1042 memcpy(msg.extradata, port->format->extradata, msg.format.extradata_size);
1043
1044 LOG_TRACE("set port info (%i:%i)", port->type, port->index);
1045
1046 status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg),
1047 MMAL_WORKER_PORT_INFO_SET, &reply, &replylen, MMAL_FALSE);
1048 if (status == MMAL_SUCCESS)
1049 {
1050 vcos_assert(replylen == sizeof(reply));
1051 status = reply.status;
1052 }
1053
1054 if (status != MMAL_SUCCESS)
1055 {
1056 LOG_ERROR("failed to set port info (%i:%i): %s", port->type, port->index,
1057 mmal_status_to_string(status));
1058 return status;
1059 }
1060
1061 port->buffer_num_min = reply.port.buffer_num_min;
1062 port->buffer_num_recommended = reply.port.buffer_num_recommended;
1063 port->buffer_num = reply.port.buffer_num;
1064 port->buffer_size_min = reply.port.buffer_size_min;
1065 port->buffer_size_recommended = reply.port.buffer_size_recommended;
1066 port->buffer_size = reply.port.buffer_size;
1067 port->buffer_alignment_min = reply.port.buffer_alignment_min;
1068 port->is_enabled = reply.port.is_enabled;
1069 port->capabilities = reply.port.capabilities;
1070
1071 mmal_vc_copy_es_format_from_vc(&reply.format, port->format);
1072
1073 *port->format->es = reply.es;
1074 if(port->format->extradata_size)
1075 {
1076 status = mmal_format_extradata_alloc(port->format, port->format->extradata_size);
1077 if(status != MMAL_SUCCESS)
1078 {
1079 vcos_assert(0);
1080 port->format->extradata_size = 0;
1081 LOG_ERROR("couldn't allocate extradata %i", port->format->extradata_size);
1082 return MMAL_ENOMEM;
1083 }
1084 memcpy(port->format->extradata, reply.extradata, port->format->extradata_size);
1085 }
1086
1087 return MMAL_SUCCESS;
1088}
1089
1090/** Set format on a port */
1091static MMAL_STATUS_T mmal_vc_port_set_format(MMAL_PORT_T *port)
1092{
1093 MMAL_COMPONENT_T *component = port->component;
1094 MMAL_COMPONENT_MODULE_T *module = component->priv->module;
1095 MMAL_STATUS_T status;
1096 unsigned int i;
1097
1098 status = mmal_vc_port_info_set(port);
1099 if (status != MMAL_SUCCESS)
1100 {
1101 LOG_ERROR("mmal_vc_port_info_set failed %p (%s)", port,
1102 mmal_status_to_string(status));
1103 return status;
1104 }
1105
1106 /* Get the setting back for this port */
1107 status = mmal_vc_port_info_get(port);
1108 if (status != MMAL_SUCCESS)
1109 {
1110 LOG_ERROR("mmal_vc_port_info_get failed %p (%s)", port,
1111 mmal_status_to_string(status));
1112 return status;
1113 }
1114
1115 /* Get the settings for the output ports in case they have changed */
1116 if (port->type == MMAL_PORT_TYPE_INPUT)
1117 {
1118 for (i = 0; i < module->ports_num; i++)
1119 {
1120 if (module->ports[i]->port->type != MMAL_PORT_TYPE_OUTPUT)
1121 continue;
1122
1123 status = mmal_vc_port_info_get(module->ports[i]->port);
1124 if (status != MMAL_SUCCESS)
1125 {
1126 LOG_ERROR("mmal_vc_port_info_get failed %p (%i)",
1127 module->ports[i]->port, status);
1128 return status;
1129 }
1130 }
1131 }
1132
1133 return MMAL_SUCCESS;
1134}
1135
1136/** Set parameter on a port */
1137static MMAL_STATUS_T mmal_vc_port_parameter_set(MMAL_PORT_T *port, const MMAL_PARAMETER_HEADER_T *param)
1138{
1139 MMAL_PORT_MODULE_T *module = port->priv->module;
1140 MMAL_STATUS_T status;
1141 mmal_worker_port_param_set msg;
1142 size_t msglen = MMAL_OFFSET(mmal_worker_port_param_set, param) + param->size;
1143 mmal_worker_reply reply;
1144 size_t replylen = sizeof(reply);
1145
1146 if(param->size > MMAL_WORKER_PORT_PARAMETER_SET_MAX)
1147 {
1148 LOG_ERROR("parameter too large (%u > %zu)", param->size, MMAL_WORKER_PORT_PARAMETER_SET_MAX);
1149 return MMAL_ENOSPC;
1150 }
1151
1152 /* Intercept the zero copy parameter */
1153 if (param->id == MMAL_PARAMETER_ZERO_COPY &&
1154 param->size >= sizeof(MMAL_PARAMETER_BOOLEAN_T) )
1155 {
1156 module->is_zero_copy = !!((MMAL_PARAMETER_BOOLEAN_T *)param)->enable;
1157 module->zero_copy_workaround = ((MMAL_PARAMETER_BOOLEAN_T *)param)->enable == 0xBEEF;
1158 LOG_DEBUG("%s zero copy on port %p", module->is_zero_copy ? "enable" : "disable", port);
1159 }
1160
1161 msg.component_handle = module->component_handle;
1162 msg.port_handle = module->port_handle;
1163 /* coverity[overrun-buffer-arg] */
1164 memcpy(&msg.param, param, param->size);
1165
1166 status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, msglen,
1167 MMAL_WORKER_PORT_PARAMETER_SET, &reply, &replylen, MMAL_FALSE);
1168
1169 if (status == MMAL_SUCCESS)
1170 {
1171 vcos_assert(replylen == sizeof(reply));
1172 status = reply.status;
1173 }
1174 if (status != MMAL_SUCCESS)
1175 {
1176 LOG_WARN("failed to set port parameter %u:%u %u:%u %s", msg.component_handle, msg.port_handle,
1177 param->id, param->size, mmal_status_to_string(status));
1178 return status;
1179 }
1180
1181 if (param->id == MMAL_PARAMETER_BUFFER_REQUIREMENTS)
1182 {
1183 /* This might have changed the buffer requirements of other ports so fetch them all */
1184 MMAL_COMPONENT_T *component = port->component;
1185 unsigned int i;
1186 for (i = 0; status == MMAL_SUCCESS && i < component->input_num; i++)
1187 status = mmal_vc_port_requirements_get(component->input[i]);
1188 for (i = 0; status == MMAL_SUCCESS && i < component->output_num; i++)
1189 status = mmal_vc_port_requirements_get(component->output[i]);
1190 }
1191
1192 return status;
1193}
1194
1195/** Get parameter on a port */
1196static MMAL_STATUS_T mmal_vc_port_parameter_get(MMAL_PORT_T *port, MMAL_PARAMETER_HEADER_T *param)
1197{
1198 MMAL_PORT_MODULE_T *module = port->priv->module;
1199 MMAL_STATUS_T status;
1200 mmal_worker_port_param_get msg;
1201 size_t msglen = MMAL_OFFSET(mmal_worker_port_param_get, param) + param->size;
1202 mmal_worker_port_param_get_reply reply;
1203 size_t replylen = MMAL_OFFSET(mmal_worker_port_param_get_reply, param) + param->size;
1204
1205 if(param->size > MMAL_WORKER_PORT_PARAMETER_GET_MAX)
1206 {
1207 LOG_ERROR("parameter too large (%u > %zu) id %u", param->size,
1208 MMAL_WORKER_PORT_PARAMETER_GET_MAX, param->id);
1209 return MMAL_ENOMEM;
1210 }
1211
1212 msg.component_handle = module->component_handle;
1213 msg.port_handle = module->port_handle;
1214 memcpy(&msg.param, param, param->size);
1215
1216 status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, msglen,
1217 MMAL_WORKER_PORT_PARAMETER_GET, &reply, &replylen, MMAL_FALSE);
1218 if (status == MMAL_SUCCESS)
1219 {
1220 status = reply.status;
1221 /* Reply must include the parameter header */
1222 vcos_assert(replylen >= MMAL_OFFSET(mmal_worker_port_param_get_reply, space));
1223
1224 /* If the call fails with MMAL_ENOSPC then reply.param.size is set to the size required for
1225 * the call to succeed, and that may be bigger than the buffers, so only check these asserts
1226 * if the call succeeded.
1227 */
1228 if ( status == MMAL_SUCCESS )
1229 {
1230 /* Reply mustn't be bigger than the parameter given */
1231 vcos_assert(replylen <= (MMAL_OFFSET(mmal_worker_port_param_get_reply, param) + param->size));
1232 /* Reply must be consistent with the parameter size embedded in it */
1233 vcos_assert(replylen == (MMAL_OFFSET(mmal_worker_port_param_get_reply, param) + reply.param.size));
1234 }
1235 }
1236
1237 if (status != MMAL_SUCCESS && status != MMAL_ENOSPC)
1238 {
1239 LOG_WARN("failed to get port parameter %u:%u %u:%u %s", msg.component_handle, msg.port_handle,
1240 param->id, param->size, mmal_status_to_string(status));
1241 return status;
1242 }
1243
1244 if (status == MMAL_ENOSPC)
1245 {
1246 /* Copy only as much as we have space for but report true size of parameter */
1247 /* coverity[overrun-buffer-arg] */
1248 memcpy(param, &reply.param, param->size);
1249 param->size = reply.param.size;
1250 }
1251 else
1252 {
1253 memcpy(param, &reply.param, reply.param.size);
1254 }
1255
1256 return status;
1257}
1258
1259static uint8_t *mmal_vc_port_payload_alloc(MMAL_PORT_T *port, uint32_t payload_size)
1260{
1261 MMAL_PORT_MODULE_T *module = port->priv->module;
1262 MMAL_BOOL_T can_deref = MMAL_TRUE;
1263 char buf[5];
1264 void *ret;
1265 (void)buf;
1266
1267 LOG_TRACE("%s: allocating %d bytes, format %s, is_zero_copy %d",
1268 port->name,
1269 payload_size,
1270 mmal_4cc_to_string(buf, sizeof(buf), port->format->encoding),
1271 module->is_zero_copy);
1272
1273 if (port->format->encoding == MMAL_ENCODING_OPAQUE &&
1274 module->is_zero_copy)
1275 {
1276 MMAL_OPAQUE_IMAGE_HANDLE_T h = mmal_vc_opaque_alloc_desc(port->name);
1277 can_deref = MMAL_FALSE;
1278 ret = (void*)(uintptr_t)h;
1279 if (!ret)
1280 {
1281 LOG_ERROR("%s: failed to allocate %d bytes opaque memory",
1282 port->name, payload_size);
1283 return NULL;
1284 }
1285 module->opaque_allocs++;
1286 }
1287
1288 else if (module->is_zero_copy)
1289 {
1290 ret = mmal_vc_shm_alloc(payload_size);
1291 if (!ret)
1292 {
1293 LOG_ERROR("%s: failed to allocate %d bytes of shared memory",
1294 port->name, payload_size);
1295 return NULL;
1296 }
1297 }
1298
1299 else
1300 {
1301 /* Allocate conventional memory */
1302 ret = vcos_calloc(1, payload_size, "mmal_vc_port payload");
1303 if (!ret)
1304 {
1305 LOG_ERROR("could not allocate %i bytes", (int)payload_size);
1306 return NULL;
1307 }
1308 }
1309
1310 /* Ensure that newly minted opaque buffers are always in a sensible
1311 * state, and don't have random garbage in them.
1312 */
1313 if (can_deref && port->format->encoding == MMAL_ENCODING_OPAQUE)
1314 memset(ret, 0, payload_size);
1315
1316 LOG_DEBUG("%s: allocated at %p", port->name, ret);
1317 return ret;
1318}
1319
1320static void mmal_vc_port_payload_free(MMAL_PORT_T *port, uint8_t *payload)
1321{
1322 MMAL_PORT_MODULE_T *module = port->priv->module;
1323
1324 if (module->opaque_allocs)
1325 {
1326 module->opaque_allocs--;
1327 mmal_vc_opaque_release((MMAL_OPAQUE_IMAGE_HANDLE_T)(uintptr_t)payload);
1328 return;
1329 }
1330
1331 else if (mmal_vc_shm_free(payload) == MMAL_SUCCESS)
1332 return;
1333
1334 /* We're dealing with conventional memory */
1335 vcos_free(payload);
1336}
1337
1338/** Create a component given its name. */
1339static MMAL_STATUS_T mmal_vc_component_create(const char *name, MMAL_COMPONENT_T *component)
1340{
1341 MMAL_STATUS_T status;
1342 const char *basename;
1343 mmal_worker_component_create msg;
1344 mmal_worker_component_create_reply reply;
1345 size_t replylen = sizeof(reply);
1346 MMAL_COMPONENT_MODULE_T *module = NULL;
1347 unsigned int ports_num, i;
1348
1349 LOG_TRACE("%s", name);
1350
1351 if (strstr(name, VIDEOCORE_PREFIX ".") != name)
1352 return MMAL_ENOSYS;
1353
1354 basename = name + sizeof(VIDEOCORE_PREFIX ".") - 1;
1355 if (strlen(basename) >= sizeof(msg.name)-1)
1356 {
1357 vcos_assert(0);
1358 return MMAL_EINVAL;
1359 }
1360
1361 /* coverity[secure_coding] Length tested above */
1362 strcpy(msg.name, basename);
1363#ifdef __linux__
1364 msg.pid = getpid();
1365#endif
1366
1367 status = mmal_vc_init();
1368 if (status != MMAL_SUCCESS)
1369 {
1370 LOG_ERROR("failed to initialise mmal ipc for '%s' (%i:%s)",
1371 name, status, mmal_status_to_string(status));
1372 return status;
1373 }
1374 status = mmal_vc_shm_init();
1375 if (status != MMAL_SUCCESS)
1376 {
1377 LOG_ERROR("failed to initialise shm for '%s' (%i:%s)",
1378 name, status, mmal_status_to_string(status));
1379 mmal_vc_deinit();
1380 return status;
1381 }
1382
1383 msg.client_component = mmal_vc_allocate_client_component(component);
1384
1385 // claim VC for entire duration of component.
1386 status = mmal_vc_use();
1387
1388 status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg),
1389 MMAL_WORKER_COMPONENT_CREATE, &reply, &replylen, MMAL_FALSE);
1390
1391 vcos_log_info("%s: %s: handle 0x%x status %d reply status %d",
1392 __FUNCTION__, name, reply.component_handle, status, reply.status);
1393
1394 if (status == MMAL_SUCCESS)
1395 {
1396 vcos_assert(replylen == sizeof(reply));
1397 status = reply.status;
1398 }
1399
1400 if (status != MMAL_SUCCESS)
1401 {
1402 LOG_ERROR("failed to create component '%s' (%i:%s)", name, status,
1403 mmal_status_to_string(status));
1404 mmal_vc_release();
1405 mmal_vc_release_client_component(component);
1406 mmal_vc_shm_exit();
1407 mmal_vc_deinit();
1408 return status;
1409 }
1410
1411 /* Component has been created, allocate our context. */
1412 status = MMAL_ENOMEM;
1413 ports_num = 1 + reply.input_num + reply.output_num + reply.clock_num;
1414 module = vcos_calloc(1, sizeof(*module) + ports_num * sizeof(*module->ports), "mmal_vc_module");
1415 if (!module)
1416 {
1417 mmal_worker_component_destroy msg;
1418 mmal_worker_reply reply;
1419 size_t replylen = sizeof(reply);
1420 MMAL_STATUS_T destroy_status;
1421
1422 destroy_status = mmal_vc_sendwait_message(mmal_vc_get_client(), &msg.header, sizeof(msg),
1423 MMAL_WORKER_COMPONENT_DESTROY, &reply, &replylen, MMAL_FALSE);
1424 vcos_assert(destroy_status == MMAL_SUCCESS);
1425 mmal_vc_release();
1426 mmal_vc_release_client_component(component);
1427 mmal_vc_shm_exit();
1428 mmal_vc_deinit();
1429 return status;
1430 }
1431 module->ports = (MMAL_PORT_MODULE_T **)&module[1];
1432 module->component_handle = reply.component_handle;
1433 component->priv->module = module;
1434
1435 /* Allocate our local ports. Control port reallocated to set module size. */
1436 mmal_port_free(component->control);
1437 component->control = mmal_port_alloc(component, MMAL_PORT_TYPE_CONTROL,
1438 sizeof(MMAL_PORT_MODULE_T));
1439 if (!component->control)
1440 goto fail;
1441
1442 if (reply.input_num)
1443 {
1444 component->input = mmal_ports_alloc(component, reply.input_num, MMAL_PORT_TYPE_INPUT,
1445 sizeof(MMAL_PORT_MODULE_T));
1446 if (!component->input)
1447 goto fail;
1448 }
1449 component->input_num = reply.input_num;
1450
1451 if (reply.output_num)
1452 {
1453 component->output = mmal_ports_alloc(component, reply.output_num, MMAL_PORT_TYPE_OUTPUT,
1454 sizeof(MMAL_PORT_MODULE_T));
1455 if (!component->output)
1456 goto fail;
1457 }
1458 component->output_num = reply.output_num;
1459
1460 if (reply.clock_num)
1461 {
1462 component->clock = mmal_ports_alloc(component, reply.clock_num, MMAL_PORT_TYPE_CLOCK,
1463 sizeof(MMAL_PORT_MODULE_T));
1464 if (!component->clock)
1465 goto fail;
1466 }
1467 component->clock_num = reply.clock_num;
1468
1469 /* We want to do the buffer callbacks to the client into a separate thread.
1470 * We'll need to queue these callbacks and have an action which does the actual callback. */
1471 module->callback_queue = mmal_queue_create();
1472 if (!module->callback_queue)
1473 goto fail;
1474 status = mmal_component_action_register(component, mmal_vc_do_callback_loop);
1475 if (status != MMAL_SUCCESS)
1476 goto fail;
1477
1478 LOG_TRACE(" handle %i", reply.component_handle);
1479
1480 module->ports[module->ports_num] = component->control->priv->module;
1481 module->ports[module->ports_num]->port = component->control;
1482 module->ports[module->ports_num]->component_handle = module->component_handle;
1483 module->ports_num++;
1484
1485 for (i = 0; i < component->input_num; i++, module->ports_num++)
1486 {
1487 module->ports[module->ports_num] = component->input[i]->priv->module;
1488 module->ports[module->ports_num]->port = component->input[i];
1489 module->ports[module->ports_num]->component_handle = module->component_handle;
1490 }
1491
1492 for (i = 0; i < component->output_num; i++, module->ports_num++)
1493 {
1494 module->ports[module->ports_num] = component->output[i]->priv->module;
1495 module->ports[module->ports_num]->port = component->output[i];
1496 module->ports[module->ports_num]->component_handle = module->component_handle;
1497 }
1498
1499 for (i = 0; i < component->clock_num; i++, module->ports_num++)
1500 {
1501 module->ports[module->ports_num] = component->clock[i]->priv->module;
1502 module->ports[module->ports_num]->port = component->clock[i];
1503 module->ports[module->ports_num]->component_handle = module->component_handle;
1504 }
1505
1506 /* Get the ports info */
1507 for (i = 0; i < module->ports_num; i++)
1508 {
1509 MMAL_PORT_T *port = module->ports[i]->port;
1510 port->priv->pf_set_format = mmal_vc_port_set_format;
1511 port->priv->pf_enable = mmal_vc_port_enable;
1512 port->priv->pf_disable = mmal_vc_port_disable;
1513 port->priv->pf_send = mmal_vc_port_send;
1514 port->priv->pf_flush = mmal_vc_port_flush;
1515 port->priv->pf_connect = mmal_vc_port_connect;
1516 port->priv->pf_parameter_set = mmal_vc_port_parameter_set;
1517 port->priv->pf_parameter_get = mmal_vc_port_parameter_get;
1518 port->priv->pf_payload_alloc = mmal_vc_port_payload_alloc;
1519 port->priv->pf_payload_free = mmal_vc_port_payload_free;
1520 port->priv->module->component_handle = module->component_handle;
1521 port->priv->module->magic = MMAL_MAGIC;
1522
1523 status = mmal_vc_port_info_get(port);
1524 if (status != MMAL_SUCCESS)
1525 goto fail;
1526 }
1527
1528 /* Initialise the vc client context which will be used for our event buffers */
1529 module->event_ctx_initialised = MMAL_FALSE;
1530 module->event_ctx.magic = MMAL_MAGIC;
1531 module->event_ctx.callback_event = mmal_vc_port_send_event_callback;
1532 module->event_ctx_handle = mmal_vc_allocate_client_context(&module->event_ctx);
1533
1534 /* populate component structure */
1535 component->priv->pf_enable = mmal_vc_component_enable;
1536 component->priv->pf_disable = mmal_vc_component_disable;
1537 component->priv->pf_destroy = mmal_vc_component_destroy;
1538 return MMAL_SUCCESS;
1539
1540fail:
1541 mmal_vc_component_destroy(component);
1542 return status;
1543}
1544
1545MMAL_CONSTRUCTOR(mmal_register_component_videocore);
1546void mmal_register_component_videocore(void)
1547{
1548 mmal_component_supplier_register(VIDEOCORE_PREFIX, mmal_vc_component_create);
1549}
1550
1551