| 1 | /* |
| 2 | Copyright (c) 2012, Broadcom Europe Ltd |
| 3 | All rights reserved. |
| 4 | |
| 5 | Redistribution and use in source and binary forms, with or without |
| 6 | modification, are permitted provided that the following conditions are met: |
| 7 | * Redistributions of source code must retain the above copyright |
| 8 | notice, this list of conditions and the following disclaimer. |
| 9 | * Redistributions in binary form must reproduce the above copyright |
| 10 | notice, this list of conditions and the following disclaimer in the |
| 11 | documentation and/or other materials provided with the distribution. |
| 12 | * Neither the name of the copyright holder nor the |
| 13 | names of its contributors may be used to endorse or promote products |
| 14 | derived from this software without specific prior written permission. |
| 15 | |
| 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
| 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 26 | */ |
| 27 | |
| 28 | #include "mmal.h" |
| 29 | #include "util/mmal_util.h" |
| 30 | #include "util/mmal_component_wrapper.h" |
| 31 | #include "mmal_logging.h" |
| 32 | #include <stdio.h> |
| 33 | |
| 34 | typedef struct |
| 35 | { |
| 36 | MMAL_WRAPPER_T wrapper; /**< Must be the first member! */ |
| 37 | |
| 38 | VCOS_SEMAPHORE_T sema; |
| 39 | |
| 40 | } MMAL_WRAPPER_PRIVATE_T; |
| 41 | |
| 42 | /** Callback from a control port. Error events will be received there. */ |
| 43 | static void mmal_wrapper_control_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 44 | { |
| 45 | MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)port->userdata; |
| 46 | LOG_TRACE("%s(%p),%p,%4.4s" , port->name, port, buffer, (char *)&buffer->cmd); |
| 47 | |
| 48 | if (buffer->cmd == MMAL_EVENT_ERROR) |
| 49 | { |
| 50 | private->wrapper.status = *(MMAL_STATUS_T *)buffer->data; |
| 51 | mmal_buffer_header_release(buffer); |
| 52 | |
| 53 | vcos_semaphore_post(&private->sema); |
| 54 | |
| 55 | if (private->wrapper.callback) |
| 56 | private->wrapper.callback(&private->wrapper); |
| 57 | return; |
| 58 | } |
| 59 | |
| 60 | mmal_buffer_header_release(buffer); |
| 61 | } |
| 62 | |
| 63 | /** Callback from an input port. Buffer is released. */ |
| 64 | static void mmal_wrapper_bh_in_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 65 | { |
| 66 | MMAL_PARAM_UNUSED(port); |
| 67 | LOG_TRACE("(%s)%p,%p,%p,%i" , port->name, port, buffer, buffer->data, (int)buffer->length); |
| 68 | |
| 69 | /* We're done with the buffer, just recycle it */ |
| 70 | mmal_buffer_header_release(buffer); |
| 71 | } |
| 72 | |
| 73 | /** Callback from an output port. Buffer is queued for the next component. */ |
| 74 | static void mmal_wrapper_bh_out_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 75 | { |
| 76 | MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)port->userdata; |
| 77 | LOG_TRACE("(%s)%p,%p,%p,%i" , port->name, port, buffer, buffer->data, (int)buffer->length); |
| 78 | |
| 79 | /* Queue the buffer produced by the output port */ |
| 80 | mmal_queue_put(private->wrapper.output_queue[port->index], buffer); |
| 81 | vcos_semaphore_post(&private->sema); |
| 82 | |
| 83 | if (private->wrapper.callback) |
| 84 | private->wrapper.callback(&private->wrapper); |
| 85 | } |
| 86 | |
| 87 | /** Callback from the pool. Buffer is available. */ |
| 88 | static MMAL_BOOL_T mmal_wrapper_bh_release_cb(MMAL_POOL_T *pool, MMAL_BUFFER_HEADER_T *buffer, |
| 89 | void *userdata) |
| 90 | { |
| 91 | MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)userdata; |
| 92 | |
| 93 | mmal_queue_put(pool->queue, buffer); |
| 94 | vcos_semaphore_post(&private->sema); |
| 95 | |
| 96 | if (private->wrapper.callback) |
| 97 | private->wrapper.callback(&private->wrapper); |
| 98 | |
| 99 | return 0; |
| 100 | } |
| 101 | |
| 102 | /*****************************************************************************/ |
| 103 | MMAL_STATUS_T mmal_wrapper_destroy(MMAL_WRAPPER_T *wrapper) |
| 104 | { |
| 105 | MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)wrapper; |
| 106 | unsigned int i; |
| 107 | |
| 108 | LOG_TRACE("%p, %s" , wrapper, wrapper->component->name); |
| 109 | |
| 110 | /* Cleanup resources */ |
| 111 | mmal_component_destroy(wrapper->component); |
| 112 | |
| 113 | for (i = 0; i < wrapper->input_num; i++) |
| 114 | { |
| 115 | if (wrapper->input_pool[i]) |
| 116 | mmal_pool_destroy(wrapper->input_pool[i]); |
| 117 | } |
| 118 | |
| 119 | for (i = 0; i < wrapper->output_num; i++) |
| 120 | { |
| 121 | if (wrapper->output_pool[i]) |
| 122 | mmal_pool_destroy(wrapper->output_pool[i]); |
| 123 | if (wrapper->output_queue[i]) |
| 124 | mmal_queue_destroy(wrapper->output_queue[i]); |
| 125 | } |
| 126 | |
| 127 | vcos_semaphore_delete(&private->sema); |
| 128 | vcos_free(private); |
| 129 | return MMAL_SUCCESS; |
| 130 | } |
| 131 | |
| 132 | /*****************************************************************************/ |
| 133 | MMAL_STATUS_T mmal_wrapper_create(MMAL_WRAPPER_T **ctx, const char *name) |
| 134 | { |
| 135 | MMAL_STATUS_T status; |
| 136 | MMAL_COMPONENT_T *component; |
| 137 | MMAL_WRAPPER_PRIVATE_T *private; |
| 138 | MMAL_WRAPPER_T *wrapper; |
| 139 | int64_t start_time; |
| 140 | unsigned int i, ; |
| 141 | |
| 142 | LOG_TRACE("wrapper %p, name %s" , ctx, name); |
| 143 | |
| 144 | /* Sanity checking */ |
| 145 | if (!ctx || !name) |
| 146 | return MMAL_EINVAL; |
| 147 | |
| 148 | start_time = vcos_getmicrosecs(); |
| 149 | |
| 150 | status = mmal_component_create(name, &component); |
| 151 | if (status != MMAL_SUCCESS) |
| 152 | return status; |
| 153 | |
| 154 | extra_size = (component->input_num * sizeof(MMAL_POOL_T*)) + (component->output_num * (sizeof(MMAL_POOL_T*) + sizeof(MMAL_QUEUE_T*))); |
| 155 | private = vcos_calloc(1, sizeof(*private) + extra_size, "mmal wrapper" ); |
| 156 | if (!private) |
| 157 | { |
| 158 | mmal_component_destroy(component); |
| 159 | return MMAL_ENOMEM; |
| 160 | } |
| 161 | |
| 162 | if (vcos_semaphore_create(&private->sema, "mmal wrapper" , 0) != VCOS_SUCCESS) |
| 163 | { |
| 164 | mmal_component_destroy(component); |
| 165 | vcos_free(private); |
| 166 | return MMAL_ENOMEM; |
| 167 | } |
| 168 | |
| 169 | wrapper = &private->wrapper; |
| 170 | wrapper->component = component; |
| 171 | wrapper->control = component->control; |
| 172 | wrapper->input_num = component->input_num; |
| 173 | wrapper->input = component->input; |
| 174 | wrapper->output_num = component->output_num; |
| 175 | wrapper->output = component->output; |
| 176 | wrapper->input_pool = (MMAL_POOL_T **)&private[1]; |
| 177 | wrapper->output_pool = (MMAL_POOL_T **)&wrapper->input_pool[component->input_num]; |
| 178 | wrapper->output_queue = (MMAL_QUEUE_T **)&wrapper->output_pool[component->output_num]; |
| 179 | |
| 180 | /* Create our pools and queues */ |
| 181 | for (i = 0; i < wrapper->input_num; i++) |
| 182 | { |
| 183 | wrapper->input_pool[i] = mmal_port_pool_create(wrapper->input[i], 0, 0); |
| 184 | if (!wrapper->input_pool[i]) |
| 185 | goto error; |
| 186 | mmal_pool_callback_set(wrapper->input_pool[i], mmal_wrapper_bh_release_cb, (void *)wrapper); |
| 187 | |
| 188 | wrapper->input[i]->userdata = (void *)wrapper; |
| 189 | } |
| 190 | for (i = 0; i < wrapper->output_num; i++) |
| 191 | { |
| 192 | wrapper->output_pool[i] = mmal_port_pool_create(wrapper->output[i], 0, 0); |
| 193 | wrapper->output_queue[i] = mmal_queue_create(); |
| 194 | if (!wrapper->output_pool[i] || !wrapper->output_queue[i]) |
| 195 | goto error; |
| 196 | mmal_pool_callback_set(wrapper->output_pool[i], mmal_wrapper_bh_release_cb, (void *)wrapper); |
| 197 | |
| 198 | wrapper->output[i]->userdata = (void *)wrapper; |
| 199 | } |
| 200 | |
| 201 | /* Setup control port */ |
| 202 | wrapper->control->userdata = (void *)wrapper; |
| 203 | status = mmal_port_enable(wrapper->control, mmal_wrapper_control_cb); |
| 204 | if (status != MMAL_SUCCESS) |
| 205 | goto error; |
| 206 | |
| 207 | wrapper->time_setup = vcos_getmicrosecs() - start_time; |
| 208 | *ctx = wrapper; |
| 209 | return MMAL_SUCCESS; |
| 210 | |
| 211 | error: |
| 212 | mmal_wrapper_destroy(wrapper); |
| 213 | return status == MMAL_SUCCESS ? MMAL_ENOMEM : status; |
| 214 | } |
| 215 | |
| 216 | /*****************************************************************************/ |
| 217 | MMAL_STATUS_T mmal_wrapper_port_enable(MMAL_PORT_T *port, uint32_t flags) |
| 218 | { |
| 219 | MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)port->userdata; |
| 220 | MMAL_WRAPPER_T *wrapper = &private->wrapper; |
| 221 | int64_t start_time = vcos_getmicrosecs(); |
| 222 | uint32_t buffer_size; |
| 223 | MMAL_STATUS_T status; |
| 224 | MMAL_POOL_T *pool; |
| 225 | |
| 226 | LOG_TRACE("%p, %s" , wrapper, port->name); |
| 227 | |
| 228 | if (port->type != MMAL_PORT_TYPE_INPUT && port->type != MMAL_PORT_TYPE_OUTPUT) |
| 229 | return MMAL_EINVAL; |
| 230 | |
| 231 | if (port->is_enabled) |
| 232 | return MMAL_SUCCESS; |
| 233 | |
| 234 | pool = port->type == MMAL_PORT_TYPE_INPUT ? |
| 235 | wrapper->input_pool[port->index] : wrapper->output_pool[port->index]; |
| 236 | buffer_size = (flags & MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE) ? port->buffer_size : 0; |
| 237 | |
| 238 | /* FIXME: we don't support switching between shared and non-shared memory. |
| 239 | * We would need to save the flag and force a pool resize when switching. */ |
| 240 | if (flags & MMAL_WRAPPER_FLAG_PAYLOAD_USE_SHARED_MEMORY) |
| 241 | { |
| 242 | MMAL_PARAMETER_BOOLEAN_T param_zc = |
| 243 | {{MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T)}, 1}; |
| 244 | status = mmal_port_parameter_set(port, ¶m_zc.hdr); |
| 245 | if (status != MMAL_SUCCESS && status != MMAL_ENOSYS) |
| 246 | { |
| 247 | LOG_ERROR("failed to set zero copy on %s" , port->name); |
| 248 | return status; |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | /* Resize the pool */ |
| 253 | status = mmal_pool_resize(pool, port->buffer_num, buffer_size); |
| 254 | if (status != MMAL_SUCCESS) |
| 255 | { |
| 256 | LOG_ERROR("could not resize pool (%i/%i)" , (int)port->buffer_num, (int)buffer_size); |
| 257 | return status; |
| 258 | } |
| 259 | |
| 260 | /* Enable port. The callback specified here is the function which |
| 261 | * will be called when a buffer header comes back to the port. */ |
| 262 | status = mmal_port_enable(port, port->type == MMAL_PORT_TYPE_INPUT ? |
| 263 | mmal_wrapper_bh_in_cb : mmal_wrapper_bh_out_cb); |
| 264 | if (status != MMAL_SUCCESS) |
| 265 | { |
| 266 | LOG_ERROR("could not enable port" ); |
| 267 | return status; |
| 268 | } |
| 269 | |
| 270 | wrapper->time_enable += vcos_getmicrosecs() - start_time; |
| 271 | return MMAL_SUCCESS; |
| 272 | } |
| 273 | |
| 274 | /*****************************************************************************/ |
| 275 | MMAL_STATUS_T mmal_wrapper_port_disable(MMAL_PORT_T *port) |
| 276 | { |
| 277 | MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)port->userdata; |
| 278 | MMAL_WRAPPER_T *wrapper = &private->wrapper; |
| 279 | int64_t start_time = vcos_getmicrosecs(); |
| 280 | MMAL_STATUS_T status; |
| 281 | |
| 282 | LOG_TRACE("%p, %s" , wrapper, port->name); |
| 283 | |
| 284 | if (port->type != MMAL_PORT_TYPE_INPUT && port->type != MMAL_PORT_TYPE_OUTPUT) |
| 285 | return MMAL_EINVAL; |
| 286 | |
| 287 | if (!port->is_enabled) |
| 288 | return MMAL_SUCCESS; |
| 289 | |
| 290 | /* Disable port */ |
| 291 | status = mmal_port_disable(port); |
| 292 | if (status != MMAL_SUCCESS) |
| 293 | { |
| 294 | LOG_ERROR("could not disable port" ); |
| 295 | return status; |
| 296 | } |
| 297 | |
| 298 | /* Flush the queue */ |
| 299 | if (port->type == MMAL_PORT_TYPE_OUTPUT) |
| 300 | { |
| 301 | MMAL_POOL_T *pool = wrapper->output_pool[port->index]; |
| 302 | MMAL_QUEUE_T *queue = wrapper->output_queue[port->index]; |
| 303 | MMAL_BUFFER_HEADER_T *buffer; |
| 304 | |
| 305 | while ((buffer = mmal_queue_get(queue)) != NULL) |
| 306 | mmal_buffer_header_release(buffer); |
| 307 | |
| 308 | if ( !vcos_verify(mmal_queue_length(pool->queue) == pool->headers_num) ) |
| 309 | { |
| 310 | LOG_ERROR("coul dnot release all buffers" ); |
| 311 | } |
| 312 | } |
| 313 | |
| 314 | wrapper->time_disable = vcos_getmicrosecs() - start_time; |
| 315 | return status; |
| 316 | } |
| 317 | |
| 318 | /** Wait for an empty buffer to be available on a port */ |
| 319 | MMAL_STATUS_T mmal_wrapper_buffer_get_empty(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T **buffer, |
| 320 | uint32_t flags) |
| 321 | { |
| 322 | MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)port->userdata; |
| 323 | MMAL_WRAPPER_T *wrapper = &private->wrapper; |
| 324 | MMAL_POOL_T *pool; |
| 325 | |
| 326 | LOG_TRACE("%p, %s" , wrapper, port->name); |
| 327 | |
| 328 | if (!buffer || (port->type != MMAL_PORT_TYPE_INPUT && port->type != MMAL_PORT_TYPE_OUTPUT)) |
| 329 | return MMAL_EINVAL; |
| 330 | |
| 331 | pool = port->type == MMAL_PORT_TYPE_INPUT ? |
| 332 | wrapper->input_pool[port->index] : wrapper->output_pool[port->index]; |
| 333 | |
| 334 | while (wrapper->status == MMAL_SUCCESS && |
| 335 | (*buffer = mmal_queue_get(pool->queue)) == NULL) |
| 336 | { |
| 337 | if (!(flags & MMAL_WRAPPER_FLAG_WAIT)) |
| 338 | break; |
| 339 | vcos_semaphore_wait(&private->sema); |
| 340 | } |
| 341 | |
| 342 | return wrapper->status == MMAL_SUCCESS && !*buffer ? MMAL_EAGAIN : wrapper->status; |
| 343 | } |
| 344 | |
| 345 | /** Wait for a full buffer to be available on a port */ |
| 346 | MMAL_STATUS_T mmal_wrapper_buffer_get_full(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T **buffer, |
| 347 | uint32_t flags) |
| 348 | { |
| 349 | MMAL_WRAPPER_PRIVATE_T *private = (MMAL_WRAPPER_PRIVATE_T *)port->userdata; |
| 350 | MMAL_WRAPPER_T *wrapper = &private->wrapper; |
| 351 | MMAL_QUEUE_T *queue; |
| 352 | |
| 353 | LOG_TRACE("%p, %s" , wrapper, port->name); |
| 354 | |
| 355 | if (!buffer || port->type != MMAL_PORT_TYPE_OUTPUT) |
| 356 | return MMAL_EINVAL; |
| 357 | queue = wrapper->output_queue[port->index]; |
| 358 | |
| 359 | while (wrapper->status == MMAL_SUCCESS && |
| 360 | (*buffer = mmal_queue_get(queue)) == NULL) |
| 361 | { |
| 362 | if (!(flags & MMAL_WRAPPER_FLAG_WAIT)) |
| 363 | break; |
| 364 | vcos_semaphore_wait(&private->sema); |
| 365 | } |
| 366 | |
| 367 | return wrapper->status == MMAL_SUCCESS && !*buffer ? MMAL_EAGAIN : wrapper->status; |
| 368 | } |
| 369 | |