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 "mmalomx.h"
29#include "mmalomx_commands.h"
30#include "mmalomx_buffer.h"
31#include "mmalomx_logging.h"
32
33typedef struct {
34 OMX_STATETYPE state;
35 OMX_STATETYPE request;
36 uint32_t actions;
37} MMALOMX_STATE_TRANSITION_T;
38
39MMALOMX_STATE_TRANSITION_T state_transition_table[] =
40{
41 {OMX_StateInvalid, OMX_StateInvalid, 0},
42 {OMX_StateLoaded, OMX_StateIdle, MMALOMX_ACTION_CHECK_ALLOCATED|MMALOMX_ACTION_ENABLE},
43 {OMX_StateLoaded, OMX_StateWaitForResources, 0},
44 {OMX_StateWaitForResources, OMX_StateLoaded, 0},
45 {OMX_StateWaitForResources, OMX_StateIdle, MMALOMX_ACTION_CHECK_ALLOCATED|MMALOMX_ACTION_ENABLE},
46 {OMX_StateIdle, OMX_StateLoaded, MMALOMX_ACTION_CHECK_DEALLOCATED|MMALOMX_ACTION_DISABLE},
47 {OMX_StateIdle, OMX_StateExecuting, 0},
48 {OMX_StateIdle, OMX_StatePause, 0},
49 {OMX_StateExecuting, OMX_StateIdle, MMALOMX_ACTION_FLUSH|MMALOMX_ACTION_CHECK_FLUSHED},
50 {OMX_StateExecuting, OMX_StatePause, 0},
51 {OMX_StatePause, OMX_StateIdle, 0},
52 {OMX_StatePause, OMX_StateExecuting, 0},
53 {OMX_StateMax, OMX_StateMax, 0}
54};
55
56/*****************************************************************************/
57static unsigned int mmalomx_state_transition_get(OMX_STATETYPE state, OMX_STATETYPE request)
58{
59 unsigned int i;
60
61 for (i = 0; state_transition_table[i].state != OMX_StateMax; i++)
62 if (state_transition_table[i].state == state &&
63 state_transition_table[i].request == request)
64 break;
65
66 return state_transition_table[i].state != OMX_StateMax ? i : 0;
67}
68
69/*****************************************************************************/
70static void mmalomx_buffer_cb_io(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
71{
72 mmalomx_buffer_return((MMALOMX_PORT_T *)port->userdata, buffer);
73}
74
75/*****************************************************************************/
76static void mmalomx_commands_check_port_actions(MMALOMX_COMPONENT_T *component,
77 MMALOMX_PORT_T *port)
78{
79 uint32_t exec_actions = 0;
80 MMAL_STATUS_T status;
81
82 MMALOMX_LOCK_PORT(component, port);
83 if (!port->actions)
84 {
85 MMALOMX_UNLOCK_PORT(component, port);
86 return;
87 }
88
89 if (port->actions & MMALOMX_ACTION_FLUSH)
90 {
91 port->actions &= ~MMALOMX_ACTION_FLUSH;
92 port->actions |= MMALOMX_ACTION_PENDING_FLUSH;
93 exec_actions |= MMALOMX_ACTION_PENDING_FLUSH;
94 }
95 if ((port->actions & MMALOMX_ACTION_DISABLE) &&
96 (!port->buffers_in_transit ||
97 !(port->actions & MMALOMX_ACTION_CHECK_FLUSHED)))
98 {
99 port->actions &= ~MMALOMX_ACTION_DISABLE;
100 port->actions |= MMALOMX_ACTION_PENDING_DISABLE;
101 exec_actions |= MMALOMX_ACTION_PENDING_DISABLE;
102 }
103 if ((port->actions & MMALOMX_ACTION_ENABLE) &&
104 port->buffers)
105 {
106 /* We defer enabling the mmal port until the first buffer allocation
107 * has been done. Only at that point do we know for sure whether we
108 * are going to use shared memory or not.
109 * We might want to delay it to just before sending the event to the client ???
110 */
111 port->actions &= ~MMALOMX_ACTION_ENABLE;
112 port->actions |= MMALOMX_ACTION_PENDING_ENABLE;
113 exec_actions |= MMALOMX_ACTION_PENDING_ENABLE;
114 }
115 MMALOMX_UNLOCK_PORT(component, port);
116
117 if (exec_actions & MMALOMX_ACTION_PENDING_FLUSH)
118 mmal_port_flush(port->mmal);
119
120 if (exec_actions & MMALOMX_ACTION_PENDING_DISABLE)
121 {
122 mmal_port_disable(port->mmal);
123
124 /* If there was a port format changed event, we need to make sure
125 * the new format has been committed */
126 if (port->format_changed)
127 {
128 status = mmal_port_format_commit(port->mmal);
129 if (status != MMAL_SUCCESS)
130 LOG_WARN("could not commit new format (%i)", status);
131 port->format_changed = MMAL_FALSE;
132 }
133 }
134
135 if (exec_actions & MMALOMX_ACTION_PENDING_ENABLE)
136 {
137 status = mmal_port_enable(port->mmal, mmalomx_buffer_cb_io);
138 if (status == MMAL_SUCCESS)
139 status = mmal_pool_resize(port->pool, port->mmal->buffer_num, 0);
140 if (status != MMAL_SUCCESS)
141 mmalomx_callback_event_handler(component, OMX_EventError, mmalil_error_to_omx(status), 0, NULL);
142 /* FIXME: we're still going to generate a cmd complete. Not sure if that's an issue. */
143 }
144
145 MMALOMX_LOCK_PORT(component, port);
146
147 port->actions &= ~exec_actions;
148 if ((port->actions & MMALOMX_ACTION_CHECK_ALLOCATED) && port->populated)
149 port->actions &= ~MMALOMX_ACTION_CHECK_ALLOCATED;
150 if ((port->actions & MMALOMX_ACTION_CHECK_DEALLOCATED) && !port->buffers)
151 port->actions &= ~MMALOMX_ACTION_CHECK_DEALLOCATED;
152 if ((port->actions & MMALOMX_ACTION_CHECK_FLUSHED) && !port->buffers_in_transit)
153 port->actions &= ~MMALOMX_ACTION_CHECK_FLUSHED;
154 exec_actions = port->actions;
155
156 if (port->actions == MMALOMX_ACTION_NOTIFY_FLUSH ||
157 port->actions == MMALOMX_ACTION_NOTIFY_ENABLE ||
158 port->actions == MMALOMX_ACTION_NOTIFY_DISABLE)
159 port->actions = 0; /* We're done */
160
161 MMALOMX_UNLOCK_PORT(component, port);
162
163 if (exec_actions == MMALOMX_ACTION_NOTIFY_FLUSH)
164 mmalomx_callback_event_handler(component, OMX_EventCmdComplete,
165 OMX_CommandFlush, port->index, NULL);
166 else if (exec_actions == MMALOMX_ACTION_NOTIFY_ENABLE)
167 mmalomx_callback_event_handler(component, OMX_EventCmdComplete,
168 OMX_CommandPortEnable, port->index, NULL);
169 else if (exec_actions == MMALOMX_ACTION_NOTIFY_DISABLE)
170 mmalomx_callback_event_handler(component, OMX_EventCmdComplete,
171 OMX_CommandPortDisable, port->index, NULL);
172}
173
174/*****************************************************************************/
175void mmalomx_commands_actions_check(MMALOMX_COMPONENT_T *component)
176{
177 uint32_t actions_left = 0;
178 unsigned int i;
179
180 for (i = 0; i < component->ports_num; i++)
181 mmalomx_commands_check_port_actions(component, &component->ports[i]);
182
183 MMALOMX_LOCK(component);
184 for (i = 0; i < component->ports_num; i++)
185 actions_left |= component->ports[i].actions;
186
187 if (!actions_left && component->state_transition)
188 {
189 component->state = state_transition_table[component->state_transition].request;
190 component->state_transition = 0;
191 actions_left = MMALOMX_ACTION_NOTIFY_STATE;
192 }
193 MMALOMX_UNLOCK(component);
194
195 if (actions_left == MMALOMX_ACTION_NOTIFY_STATE)
196 {
197 mmalomx_callback_event_handler(component, OMX_EventCmdComplete,
198 OMX_CommandStateSet, component->state, NULL);
199 actions_left = 0;
200 }
201
202 /* If we're not currently processing a command, we can start processing
203 * the next one. */
204 if (!actions_left)
205 mmalomx_commands_actions_next(component);
206}
207
208/*****************************************************************************/
209void mmalomx_commands_actions_signal(MMALOMX_COMPONENT_T *component)
210{
211 if (component->cmd_thread_used)
212 vcos_semaphore_post(&component->cmd_sema);
213 else
214 mmalomx_commands_actions_check(component);
215}
216
217/*****************************************************************************/
218OMX_ERRORTYPE mmalomx_command_state_set(
219 OMX_HANDLETYPE hComponent,
220 OMX_STATETYPE state)
221{
222 MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent;
223 unsigned int i, transition;
224
225 if (component->state == state)
226 {
227 mmalomx_callback_event_handler(component, OMX_EventError, OMX_ErrorSameState, 0, NULL);
228 return OMX_ErrorNone;
229 }
230
231 /* We're asked to transition to StateInvalid */
232 if (state == OMX_StateInvalid)
233 {
234 component->state = state;
235 mmalomx_callback_event_handler(component, OMX_EventError, OMX_ErrorInvalidState, 0, NULL);
236 return OMX_ErrorNone;
237 }
238
239 /* Commands are being queued so we should never get into that state */
240 vcos_assert(!component->state_transition);
241
242 /* Check the transition is valid */
243 transition = mmalomx_state_transition_get(component->state, state);
244 if (!transition)
245 {
246 mmalomx_callback_event_handler(component, OMX_EventError, OMX_ErrorIncorrectStateTransition, 0, NULL);
247 return OMX_ErrorNone;
248 }
249
250 /* Special case for transition in and out of Executing */
251 if (state == OMX_StateExecuting || component->state == OMX_StateExecuting)
252 {
253 MMAL_STATUS_T status;
254
255 if (state == OMX_StateExecuting)
256 status = mmal_component_enable(component->mmal);
257 else
258 status = mmal_component_disable(component->mmal);
259
260 if (status != MMAL_SUCCESS)
261 {
262 LOG_ERROR("could not %s %s", state == OMX_StateExecuting ? "enable" : "disable", component->name);
263 mmalomx_callback_event_handler(component, OMX_EventError, mmalil_error_to_omx(status), 0, NULL);
264 return OMX_ErrorNone;
265 }
266 }
267
268 MMALOMX_LOCK(component);
269 component->state_transition = transition;
270
271 for (i = 0; i < component->ports_num; i++)
272 {
273 if (!component->ports[i].enabled)
274 continue;
275
276 MMALOMX_LOCK_PORT(component, component->ports + i);
277 component->ports[i].actions = state_transition_table[transition].actions;
278
279 /* If we're transitionning from Idle to Loaded we'd rather do a flush first
280 * to avoid the cmd thread to block for too long (mmal_disable is a
281 * blocking call). */
282 if (state_transition_table[transition].state == OMX_StateIdle &&
283 state_transition_table[transition].request == OMX_StateLoaded &&
284 component->cmd_thread_used)
285 component->ports[i].actions |= MMALOMX_ACTION_FLUSH|MMALOMX_ACTION_CHECK_FLUSHED;
286 MMALOMX_UNLOCK_PORT(component, component->ports + i);
287 }
288 MMALOMX_UNLOCK(component);
289
290 mmalomx_commands_actions_check(component);
291 return OMX_ErrorNone;
292}
293
294/*****************************************************************************/
295OMX_ERRORTYPE mmalomx_command_port_mark(
296 OMX_HANDLETYPE hComponent,
297 OMX_U32 nPortIndex,
298 OMX_PTR *pCmdData)
299{
300 MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent;
301 OMX_MARKTYPE *mark = (OMX_MARKTYPE *)pCmdData;
302 MMALOMX_PORT_T *port;
303
304 if (nPortIndex >= component->ports_num)
305 return OMX_ErrorBadPortIndex;
306 port = &component->ports[nPortIndex];
307
308 if (port->marks_num == MAX_MARKS_NUM)
309 return OMX_ErrorInsufficientResources;
310
311 port->marks[(port->marks_first + port->marks_num) % MAX_MARKS_NUM] = *mark;
312 port->marks_num++;
313
314 return OMX_ErrorNone;
315}
316
317/*****************************************************************************/
318OMX_ERRORTYPE mmalomx_command_port_flush(
319 OMX_HANDLETYPE hComponent,
320 OMX_U32 nPortIndex)
321{
322 MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent;
323
324 MMALOMX_LOCK_PORT(component, &component->ports[nPortIndex]);
325 component->ports[nPortIndex].actions =
326 MMALOMX_ACTION_FLUSH|MMALOMX_ACTION_CHECK_FLUSHED|MMALOMX_ACTION_NOTIFY_FLUSH;
327 MMALOMX_UNLOCK_PORT(component, &component->ports[nPortIndex]);
328
329 mmalomx_commands_actions_check(component);
330
331 return OMX_ErrorNone;
332}
333
334/*****************************************************************************/
335OMX_ERRORTYPE mmalomx_command_port_enable(
336 OMX_HANDLETYPE hComponent,
337 OMX_U32 nPortIndex)
338{
339 MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent;
340 component->ports[nPortIndex].enabled = MMAL_TRUE;
341
342 if (component->state == OMX_StateLoaded ||
343 component->state == OMX_StateWaitForResources)
344 {
345 mmalomx_callback_event_handler(component, OMX_EventCmdComplete, OMX_CommandPortEnable, nPortIndex, NULL);
346 return OMX_ErrorNone;
347 }
348
349 MMALOMX_LOCK_PORT(component, &component->ports[nPortIndex]);
350 component->ports[nPortIndex].actions =
351 MMALOMX_ACTION_CHECK_ALLOCATED|MMALOMX_ACTION_ENABLE|MMALOMX_ACTION_NOTIFY_ENABLE;
352 MMALOMX_UNLOCK_PORT(component, &component->ports[nPortIndex]);
353
354 mmalomx_commands_actions_check(component);
355
356 return OMX_ErrorNone;
357}
358
359/*****************************************************************************/
360OMX_ERRORTYPE mmalomx_command_port_disable(
361 OMX_HANDLETYPE hComponent,
362 OMX_U32 nPortIndex)
363{
364 MMALOMX_COMPONENT_T *component = (MMALOMX_COMPONENT_T *)hComponent;
365 component->ports[nPortIndex].enabled = MMAL_FALSE;
366
367 if (component->state == OMX_StateLoaded ||
368 component->state == OMX_StateWaitForResources)
369 {
370 mmalomx_callback_event_handler(component, OMX_EventCmdComplete, OMX_CommandPortDisable, nPortIndex, NULL);
371 return OMX_ErrorNone;
372 }
373
374 MMALOMX_LOCK_PORT(component, &component->ports[nPortIndex]);
375 component->ports[nPortIndex].actions =
376 MMALOMX_ACTION_DISABLE|MMALOMX_ACTION_CHECK_DEALLOCATED|MMALOMX_ACTION_NOTIFY_DISABLE;
377 if (component->cmd_thread_used)
378 component->ports[nPortIndex].actions |=
379 MMALOMX_ACTION_FLUSH|MMALOMX_ACTION_CHECK_FLUSHED;
380 MMALOMX_UNLOCK_PORT(component, &component->ports[nPortIndex]);
381
382 mmalomx_commands_actions_check(component);
383
384 return OMX_ErrorNone;
385}
386
387/*****************************************************************************/
388OMX_ERRORTYPE mmalomx_command_queue(
389 MMALOMX_COMPONENT_T *component,
390 OMX_U32 arg1, OMX_U32 arg2)
391{
392 MMAL_BUFFER_HEADER_T *cmd = mmal_queue_get(component->cmd_pool->queue);
393
394 if (!vcos_verify(cmd))
395 {
396 LOG_ERROR("command queue too small");
397 return OMX_ErrorInsufficientResources;
398 }
399
400 cmd->cmd = arg1;
401 cmd->offset = arg2;
402 mmal_queue_put(component->cmd_queue, cmd);
403
404 mmalomx_commands_actions_signal(component);
405
406 return OMX_ErrorNone;
407}
408
409/*****************************************************************************/
410OMX_ERRORTYPE mmalomx_command_dequeue(
411 MMALOMX_COMPONENT_T *component,
412 OMX_U32 *arg1, OMX_U32 *arg2)
413{
414 MMAL_BUFFER_HEADER_T *cmd = mmal_queue_get(component->cmd_queue);
415 if (!cmd)
416 return OMX_ErrorNoMore;
417
418 *arg1 = cmd->cmd;
419 *arg2 = cmd->offset;
420 mmal_buffer_header_release(cmd);
421 return OMX_ErrorNone;
422}
423
424/*****************************************************************************/
425void mmalomx_commands_actions_next(MMALOMX_COMPONENT_T *component)
426{
427 OMX_ERRORTYPE status = OMX_ErrorNone;
428 OMX_COMMANDTYPE cmd;
429 OMX_U32 arg1, arg2, nParam1;
430 unsigned int i;
431
432 status = mmalomx_command_dequeue(component, &arg1, &arg2);
433 if (status != OMX_ErrorNone)
434 return;
435
436 cmd = (OMX_COMMANDTYPE)arg1;
437 nParam1 = arg2;
438
439 if (cmd == OMX_CommandStateSet)
440 {
441 mmalomx_command_state_set((OMX_HANDLETYPE)&component->omx, nParam1);
442 }
443 else if (cmd == OMX_CommandFlush)
444 {
445 for (i = 0; i < component->ports_num; i++)
446 if (i == nParam1 || nParam1 == OMX_ALL)
447 mmalomx_command_port_flush((OMX_HANDLETYPE)&component->omx, i);
448 }
449 else if (cmd == OMX_CommandPortEnable)
450 {
451 for (i = 0; i < component->ports_num; i++)
452 if (i == nParam1 || nParam1 == OMX_ALL)
453 mmalomx_command_port_enable((OMX_HANDLETYPE)&component->omx, i);
454 }
455 else if (cmd == OMX_CommandPortDisable)
456 {
457 for (i = 0; i < component->ports_num; i++)
458 if (i == nParam1 || nParam1 == OMX_ALL)
459 mmalomx_command_port_disable((OMX_HANDLETYPE)&component->omx, i);
460 }
461}
462
463