1 | /* |
2 | Copyright (c) 2012, Broadcom Europe Ltd |
3 | All rights reserved. |
4 | |
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the following conditions are met: |
7 | * Redistributions of source code must retain the above copyright |
8 | notice, this list of conditions and the following disclaimer. |
9 | * Redistributions in binary form must reproduce the above copyright |
10 | notice, this list of conditions and the following disclaimer in the |
11 | documentation and/or other materials provided with the distribution. |
12 | * Neither the name of the copyright holder nor the |
13 | names of its contributors may be used to endorse or promote products |
14 | derived from this software without specific prior written permission. |
15 | |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ |
27 | |
28 | #include "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 | |
44 | typedef enum MMAL_ZEROLEN_CHECK_T |
45 | { |
46 | ZEROLEN_NOT_INITIALIZED, |
47 | ZEROLEN_COMPATIBLE, |
48 | ZEROLEN_INCOMPATIBLE |
49 | } MMAL_ZEROLEN_CHECK_T; |
50 | |
51 | typedef 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 | |
58 | typedef 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 | |
77 | typedef 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 | *****************************************************************************/ |
95 | static void mmal_vc_do_callback(MMAL_COMPONENT_T *component); |
96 | static MMAL_STATUS_T mmal_vc_port_info_get(MMAL_PORT_T *port); |
97 | |
98 | /*****************************************************************************/ |
99 | MMAL_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 | /*****************************************************************************/ |
121 | MMAL_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. */ |
142 | static 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. */ |
172 | static 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 */ |
209 | static 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 */ |
290 | static 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 */ |
332 | static 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 */ |
360 | static 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 */ |
396 | static 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 */ |
437 | static 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 | /*****************************************************************************/ |
491 | static 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 | |
520 | static 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 | */ |
532 | static 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 | |
556 | static 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 | */ |
567 | static 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 | |
713 | static 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; |
741 | fail: |
742 | return status; |
743 | } |
744 | |
745 | static 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 | |
774 | static 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 | |
813 | fail: |
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 | |
822 | MMAL_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 | |
844 | MMAL_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 | |
865 | MMAL_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 | |
881 | MMAL_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 | |
906 | MMAL_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 | |
943 | static 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. */ |
956 | static 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. */ |
1016 | static 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 */ |
1091 | static 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 */ |
1137 | static 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 */ |
1196 | static 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 | |
1259 | static 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 | |
1320 | static 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. */ |
1339 | static 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 | |
1540 | fail: |
1541 | mmal_vc_component_destroy(component); |
1542 | return status; |
1543 | } |
1544 | |
1545 | MMAL_CONSTRUCTOR(mmal_register_component_videocore); |
1546 | void mmal_register_component_videocore(void) |
1547 | { |
1548 | mmal_component_supplier_register(VIDEOCORE_PREFIX, mmal_vc_component_create); |
1549 | } |
1550 | |
1551 | |