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 "mmalomx.h" |
29 | #include "mmalomx_commands.h" |
30 | #include "mmalomx_buffer.h" |
31 | #include "mmalomx_logging.h" |
32 | |
33 | typedef struct { |
34 | OMX_STATETYPE state; |
35 | OMX_STATETYPE request; |
36 | uint32_t actions; |
37 | } MMALOMX_STATE_TRANSITION_T; |
38 | |
39 | MMALOMX_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 | /*****************************************************************************/ |
57 | static 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 | /*****************************************************************************/ |
70 | static 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 | /*****************************************************************************/ |
76 | static 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 | /*****************************************************************************/ |
175 | void 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 | /*****************************************************************************/ |
209 | void 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 | /*****************************************************************************/ |
218 | OMX_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 | /*****************************************************************************/ |
295 | OMX_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 | /*****************************************************************************/ |
318 | OMX_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 | /*****************************************************************************/ |
335 | OMX_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 | /*****************************************************************************/ |
360 | OMX_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 | /*****************************************************************************/ |
388 | OMX_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 | /*****************************************************************************/ |
410 | OMX_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 | /*****************************************************************************/ |
425 | void 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 | |