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 <stdio.h>
29
30/* Project includes */
31#include "vcinclude/common.h"
32
33#include "interface/vchiq_arm/vchiq.h"
34#include "interface/vmcs_host/khronos/IL/OMX_Component.h"
35#include "interface/vmcs_host/vc_ilcs_defs.h"
36#include "interface/vmcs_host/vcilcs.h"
37
38#ifdef USE_VCHI
39#include "interface/vchi/vchiq_wrapper.h"
40#endif
41
42#ifdef _VIDEOCORE
43#include "applications/vmcs/ilcs/ilcs_common.h"
44#endif
45
46/******************************************************************************
47Private types and defines.
48******************************************************************************/
49
50// number of threads that can use ilcs
51#define ILCS_MAX_WAITING 8
52
53// maximum number of retries to grab a wait slot,
54// before the message is discarded and failure returned.
55#define ILCS_WAIT_TIMEOUT 250
56
57// maximum number of concurrent function calls that can
58// be going at once. Each function call requires to copy
59// the message data so we can dequeue the message from vchi
60// before executing the function, otherwise ILCS may cause
61// a deadlock. Must be larger than ILCS_MAX_WAITING
62#define ILCS_MAX_NUM_MSGS (ILCS_MAX_WAITING+1)
63#define ILCS_MSG_INUSE_MASK ((1<<ILCS_MAX_NUM_MSGS)-1)
64
65typedef struct {
66 int xid;
67 void *resp;
68 int *rlen;
69 VCOS_EVENT_T event;
70} ILCS_WAIT_T;
71
72typedef enum {
73 NORMAL_SERVICE = 0, // process all messages
74 ABORTED_BULK = 1, // reject incoming calls
75 CLOSED_CALLBACK = 2, // quit thread and cleanup, use reaper, VC only
76 DEINIT_CALLED = 3, // quit thread and cleanup, no reaper
77} ILCS_QUIT_T;
78
79struct ILCS_SERVICE_T {
80 char name[12];
81#ifdef USE_VCHIQ_ARM
82 VCHIQ_INSTANCE_T vchiq;
83 VCHIQ_SERVICE_HANDLE_T service;
84#else
85 VCHIQ_STATE_T *vchiq;
86#endif
87 int fourcc;
88 VCOS_TIMER_T timer;
89 volatile int timer_expired;
90 VCOS_THREAD_T thread;
91 int timer_needed;
92 ILCS_QUIT_T kill_service;
93 int use_memmgr;
94
95 ILCS_COMMON_T *ilcs_common;
96 ILCS_CONFIG_T config;
97
98 VCHIU_QUEUE_T queue;
99 VCOS_EVENT_T bulk_rx;
100
101 VCOS_SEMAPHORE_T send_sem; // for making control+bulk serialised
102 VCOS_MUTEX_T wait_mtx; // for protecting ->wait and ->next_xid
103 ILCS_WAIT_T wait[ILCS_MAX_WAITING];
104 int next_xid;
105 VCOS_EVENT_T wait_event; // for signalling when a wait becomes free
106
107 // don't need locking around msg_inuse as only touched by
108 // the server thread in ilcs_process_message
109 unsigned int msg_inuse;
110 unsigned char msg[ILCS_MAX_NUM_MSGS][VCHIQ_SLOT_SIZE];
111 uint32_t header_array[(sizeof(VCHIQ_HEADER_T)+8)/4];
112};
113
114/******************************************************************************
115Private functions in this file.
116Define as static.
117******************************************************************************/
118#ifdef USE_VCHIQ_ARM
119static VCHIQ_STATUS_T ilcs_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, VCHIQ_SERVICE_HANDLE_T service_user, void *bulk_user);
120#else
121static int ilcs_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, void *service_user, void *bulk_user);
122#endif
123static void *ilcs_task(void *param);
124static void ilcs_response(ILCS_SERVICE_T *st, uint32_t xid, const unsigned char *msg, int len );
125static void ilcs_transmit(ILCS_SERVICE_T *st, uint32_t cmd, uint32_t xid,
126 const unsigned char *msg, int len,
127 const unsigned char *msg2, int len2);
128static void ilcs_command(ILCS_SERVICE_T *st, uint32_t cmd, uint32_t xid, unsigned char *msg, int len);
129static void ilcs_timer(void *param);
130static int ilcs_process_message(ILCS_SERVICE_T *st, int block);
131
132/* ----------------------------------------------------------------------
133 * initialise OpenMAX IL component service
134 * -------------------------------------------------------------------- */
135#ifdef USE_VCHIQ_ARM
136ILCS_SERVICE_T *ilcs_init(VCHIQ_INSTANCE_T state, void **connection, ILCS_CONFIG_T *config, int use_memmgr)
137#else
138ILCS_SERVICE_T *ilcs_init(VCHIQ_STATE_T *state, void **connection, ILCS_CONFIG_T *config, int use_memmgr)
139#endif
140{
141 int32_t i;
142 VCOS_THREAD_ATTR_T thread_attrs;
143 ILCS_SERVICE_T *st;
144 VCHIQ_SERVICE_PARAMS_T params;
145
146 st = vcos_malloc(sizeof(ILCS_SERVICE_T), "ILCS State");
147 if(!st)
148 goto fail_alloc;
149
150 memset(st, 0, sizeof(ILCS_SERVICE_T));
151 st->vchiq = state;
152 st->fourcc = VCHIQ_MAKE_FOURCC('I', 'L', 'C', 'S');
153 st->config = *config;
154
155 // setting this to true implies we have relocatable handles as
156 // buffer pointers, otherwise we interpret them to be real pointers
157 st->use_memmgr = use_memmgr;
158
159 // create semaphore for protecting wait/xid structures
160 if(vcos_mutex_create(&st->wait_mtx, "ILCS") != VCOS_SUCCESS)
161 goto fail_all;
162
163 // create smaphore for control+bulk protection
164 if(vcos_semaphore_create(&st->send_sem, "ILCS", 1) != VCOS_SUCCESS)
165 goto fail_send_sem;
166
167 // create event group for signalling when a waiting slot becomes free
168 if(vcos_event_create(&st->wait_event, "ILCS") != VCOS_SUCCESS)
169 goto fail_wait_event;
170
171 for(i=0; i<ILCS_MAX_WAITING; i++)
172 if(vcos_event_create(&st->wait[i].event, "ILCS") != VCOS_SUCCESS)
173 {
174 while(--i >= 0)
175 vcos_event_delete(&st->wait[i].event);
176 goto fail_wait_events;
177 }
178
179 if(vcos_timer_create(&st->timer, "ILCS", ilcs_timer, st) != VCOS_SUCCESS)
180 goto fail_timer;
181
182 // create the queue of incoming messages
183 if(!vchiu_queue_init(&st->queue, 256))
184 goto fail_queue;
185
186 // create the bulk receive event
187 if(vcos_event_create(&st->bulk_rx, "ILCS") != VCOS_SUCCESS)
188 goto fail_bulk_event;
189
190 // create an 'ILCS' service
191#ifdef USE_VCHIQ_ARM
192 /* VCHIQ_ARM distinguishes between servers and clients. Use use_memmgr
193 parameter to detect usage by the client.
194 */
195
196 memset(&params,0,sizeof(params));
197 params.fourcc = st->fourcc;
198 params.callback = ilcs_callback;
199 params.userdata = st;
200 params.version = VC_ILCS_VERSION;
201 params.version_min = VC_ILCS_VERSION;
202
203 if (use_memmgr == 0)
204 {
205 // Host side, which will connect to a listening VideoCore side
206 if (vchiq_open_service(st->vchiq, &params, &st->service) != VCHIQ_SUCCESS)
207 goto fail_service;
208 }
209 else
210 {
211 // VideoCore side, a listening service not connected
212 if (vchiq_add_service(st->vchiq, &params, &st->service) != VCHIQ_SUCCESS)
213 goto fail_service;
214
215 // On VC shutdown we defer calling vchiq_remove_service until after the callback has
216 // returned, so we require not to have the autoclose behaviour
217 vchiq_set_service_option(st->service, VCHIQ_SERVICE_OPTION_AUTOCLOSE, 0);
218 }
219#else
220#ifdef USE_VCHI
221 if(!vchiq_wrapper_add_service(st->vchiq, connection, st->fourcc, ilcs_callback, st))
222 goto fail_service;
223#else
224 if(!vchiq_add_service(st->vchiq, st->fourcc, ilcs_callback, st))
225 goto fail_service;
226#endif
227#endif
228
229 if((st->ilcs_common = st->config.ilcs_common_init(st)) == NULL)
230 goto fail_ilcs_common;
231
232 vcos_thread_attr_init(&thread_attrs);
233 vcos_thread_attr_setstacksize(&thread_attrs, 4096);
234
235 snprintf(st->name, sizeof(st->name), "ILCS_%s", use_memmgr ? "VC" : "HOST");
236
237 if(vcos_thread_create(&st->thread, st->name, &thread_attrs, ilcs_task, st) != VCOS_SUCCESS)
238 goto fail_thread;
239
240 return st;
241
242 fail_thread:
243 st->config.ilcs_common_deinit(st->ilcs_common);
244 fail_ilcs_common:
245#ifdef USE_VCHIQ_ARM
246 vchiq_remove_service(st->service);
247#endif
248 fail_service:
249 vcos_event_delete(&st->bulk_rx);
250 fail_bulk_event:
251 vchiu_queue_delete(&st->queue);
252 fail_queue:
253 vcos_timer_delete(&st->timer);
254 fail_timer:
255 for(i=0; i<ILCS_MAX_WAITING; i++)
256 vcos_event_delete(&st->wait[i].event);
257 fail_wait_events:
258 vcos_event_delete(&st->wait_event);
259 fail_wait_event:
260 vcos_semaphore_delete(&st->send_sem);
261 fail_send_sem:
262 vcos_mutex_delete(&st->wait_mtx);
263 fail_all:
264 vcos_free(st);
265 fail_alloc:
266 return NULL;
267}
268
269/* ----------------------------------------------------------------------
270 * sends a message to the thread to quit
271 * -------------------------------------------------------------------- */
272static void ilcs_send_quit(ILCS_SERVICE_T *st)
273{
274 // We're closing, so tell the task to cleanup
275 VCHIQ_HEADER_T *header = (VCHIQ_HEADER_T *)st->header_array;
276 char *msg;
277 int i;
278 header->size = 8;
279 msg = header->data;
280 msg[0] = IL_SERVICE_QUIT & 0xff;
281 msg[1] = (IL_SERVICE_QUIT >> 8) & 0xff;
282 msg[2] = (IL_SERVICE_QUIT >> 16) & 0xff;
283 msg[3] = IL_SERVICE_QUIT >> 24;
284
285 vchiu_queue_push(&st->queue, header);
286
287 // force all currently waiting clients to wake up
288 for(i=0; i<ILCS_MAX_WAITING; i++)
289 if(st->wait[i].resp)
290 vcos_event_signal(&st->wait[i].event);
291
292 vcos_event_signal(&st->wait_event);
293}
294
295/* ----------------------------------------------------------------------
296 * deinitialises the OpenMAX IL Component Service.
297 * This is the usual way that the host side service shuts down, called
298 * from OMX_Deinit().
299 * -------------------------------------------------------------------- */
300void ilcs_deinit(ILCS_SERVICE_T *st)
301{
302 void *data;
303 st->kill_service = DEINIT_CALLED;
304 ilcs_send_quit(st);
305 vcos_thread_join(&st->thread, &data);
306 vcos_free(st);
307}
308
309/* ----------------------------------------------------------------------
310 * sets the wait event, to timeout blocked threads
311 * -------------------------------------------------------------------- */
312static void ilcs_timer(void *param)
313{
314 ILCS_SERVICE_T *st = (ILCS_SERVICE_T *) param;
315
316 vcos_assert(st->timer_expired == 0);
317 st->timer_expired = 1;
318 vcos_event_signal(&st->wait_event);
319}
320
321/* ----------------------------------------------------------------------
322 * returns pointer to common object
323 * -------------------------------------------------------------------- */
324ILCS_COMMON_T *ilcs_get_common(ILCS_SERVICE_T *st)
325{
326 return st->ilcs_common;
327}
328
329/* ----------------------------------------------------------------------
330 * whether the ilcsg thread is currently running
331 * returns 1 if the ilcsg is the current thread, 0 otherwise
332 * -------------------------------------------------------------------- */
333int ilcs_thread_current(void *param)
334{
335 ILCS_SERVICE_T *st = (ILCS_SERVICE_T *) param;
336 return vcos_thread_current() == &st->thread;
337}
338
339/* ----------------------------------------------------------------------
340 * called from the vchiq layer whenever an event happens.
341 * here, we are only interested in the 'message available' callback
342 * -------------------------------------------------------------------- */
343#ifdef USE_VCHIQ_ARM
344static VCHIQ_STATUS_T ilcs_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, VCHIQ_SERVICE_HANDLE_T service_user, void *bulk_user)
345{
346 ILCS_SERVICE_T *st = (ILCS_SERVICE_T *)VCHIQ_GET_SERVICE_USERDATA(service_user);
347#else
348static int ilcs_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, void *service_user, void *bulk_user)
349{
350 ILCS_SERVICE_T *st = (ILCS_SERVICE_T *) service_user;
351#endif
352
353 switch(reason) {
354
355#ifdef USE_VCHIQ_ARM
356 case VCHIQ_SERVICE_OPENED:
357 {
358#ifdef _VIDEOCORE
359 // We're on the VideoCore side and we've been connected to, so we need to spawn another
360 // listening service. Create another ILCS instance.
361 ILCS_CONFIG_T config;
362 ilcs_config(&config);
363 ilcs_init(st->vchiq, NULL, &config, st->use_memmgr);
364#else
365 vcos_abort();
366#endif
367 }
368 break;
369
370 case VCHIQ_SERVICE_CLOSED:
371 if(st && st->kill_service < CLOSED_CALLBACK)
372 {
373 st->kill_service = CLOSED_CALLBACK;
374 ilcs_send_quit(st);
375 }
376 break;
377
378 case VCHIQ_BULK_RECEIVE_ABORTED:
379 // bulk rx only aborted if we're about to close the service,
380 // so signal this now so that the person waiting for this
381 // bulk rx can return a failure to the user
382 st->kill_service = ABORTED_BULK;
383 vcos_event_signal(&st->bulk_rx);
384 break;
385#endif
386
387 case VCHIQ_MESSAGE_AVAILABLE:
388#ifndef _VIDEOCORE
389 {
390 static int queue_warn = 0;
391 int queue_len = st->queue.write - st->queue.read;
392 if (!queue_warn)
393 queue_warn = getenv("ILCS_WARN") ? (st->queue.size/2) : st->queue.size;
394 if (queue_len >= queue_warn)
395 {
396 if (queue_len == st->queue.size)
397 VCOS_ALERT("ILCS queue full");
398 else
399 VCOS_ALERT("ILCS queue len = %d", queue_len);
400 queue_warn = queue_warn + (st->queue.size - queue_warn)/2;
401 }
402 }
403#endif
404 vchiu_queue_push(&st->queue, header);
405 break;
406
407 case VCHIQ_BULK_RECEIVE_DONE:
408 vcos_event_signal(&st->bulk_rx);
409 break;
410
411 default:
412 break;
413 }
414
415#ifdef USE_VCHIQ_ARM
416 return VCHIQ_SUCCESS;
417#else
418 return 1;
419#endif
420}
421
422/* ----------------------------------------------------------------------
423 * send a message and wait for reply.
424 * repeats continuously, on each connection
425 * -------------------------------------------------------------------- */
426static void *ilcs_task(void *param)
427{
428 ILCS_SERVICE_T *st = (ILCS_SERVICE_T *) param;
429 int i;
430
431 st->config.ilcs_thread_init(st->ilcs_common);
432
433 while(st->kill_service < CLOSED_CALLBACK)
434 ilcs_process_message(st, 1);
435
436 // tidy up after ourselves
437 st->config.ilcs_common_deinit(st->ilcs_common);
438#ifdef USE_VCHIQ_ARM
439 vchiq_remove_service(st->service);
440#endif
441 vcos_event_delete(&st->bulk_rx);
442 vchiu_queue_delete(&st->queue);
443 vcos_timer_delete(&st->timer);
444 for(i=0; i<ILCS_MAX_WAITING; i++)
445 vcos_event_delete(&st->wait[i].event);
446 vcos_event_delete(&st->wait_event);
447 vcos_semaphore_delete(&st->send_sem);
448 vcos_mutex_delete(&st->wait_mtx);
449
450 if(st->kill_service == CLOSED_CALLBACK)
451 {
452#ifdef _VIDEOCORE
453 // need vcos reaper thread to do join/free for us
454 vcos_thread_reap(&st->thread, vcos_free, st);
455#else
456 // we've got a CLOSED callback from vchiq without ilcs_deinit being called.
457 // this shouldn't really happen, so we just want to abort at this point.
458 vcos_abort();
459#endif
460 }
461
462 return 0;
463}
464
465/* ----------------------------------------------------------------------
466 * check to see if there are any pending messages
467 *
468 * if there are no messages, return 0
469 *
470 * otherwise, fetch and process the first queued message (which will
471 * be either a command or response from host)
472 * -------------------------------------------------------------------- */
473#define UINT32(p) ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24))
474
475static int ilcs_process_message(ILCS_SERVICE_T *st, int block)
476{
477 unsigned char *msg;
478 VCHIQ_HEADER_T *header;
479 uint32_t i, msg_len, cmd, xid;
480
481 if(!block && vchiu_queue_is_empty(&st->queue))
482 return 0; // no more messages
483
484 header = vchiu_queue_pop(&st->queue);
485
486 msg = (unsigned char *) header->data;
487
488 cmd = UINT32(msg);
489 xid = UINT32(msg+4);
490
491 msg += 8;
492 msg_len = header->size - 8;
493
494 if(cmd == IL_RESPONSE)
495 {
496 ilcs_response(st, xid, msg, msg_len);
497#ifdef USE_VCHIQ_ARM
498 vchiq_release_message(st->service, header);
499#else
500 vchiq_release_message(st->vchiq, header);
501#endif
502 }
503 else if(cmd == IL_SERVICE_QUIT)
504 {
505 return 1;
506 }
507 else
508 {
509 // we can only handle commands if we have space to copy the message first
510 if(st->msg_inuse == ILCS_MSG_INUSE_MASK)
511 {
512 // this shouldn't happen, since we have more msg slots than the
513 // remote side is allowed concurrent clients. this is classed
514 // as a failure case, so we discard the message knowing that things
515 // will surely lock up fairly soon after.
516 vcos_assert(0);
517 return 1;
518 }
519
520 i = 0;
521 while(st->msg_inuse & (1<<i))
522 i++;
523
524 st->msg_inuse |= (1<<i);
525
526 memcpy(st->msg[i], msg, msg_len);
527#ifdef USE_VCHIQ_ARM
528 vchiq_release_message(st->service, header);
529#else
530 vchiq_release_message(st->vchiq, header);
531#endif
532 ilcs_command(st, cmd, xid, st->msg[i], msg_len);
533
534 // mark the message copy as free
535 st->msg_inuse &= ~(1<<i);
536 }
537
538 return 1;
539}
540
541/* ----------------------------------------------------------------------
542 * received response to an ILCS command
543 * -------------------------------------------------------------------- */
544static void ilcs_response(ILCS_SERVICE_T *st, uint32_t xid, const unsigned char *msg, int len)
545{
546 ILCS_WAIT_T *wait = NULL;
547 int i, copy = len;
548
549 // atomically retrieve given ->wait entry
550 vcos_mutex_lock(&st->wait_mtx);
551 for (i=0; i<ILCS_MAX_WAITING; i++) {
552 wait = &st->wait[i];
553 if(wait->resp && wait->xid == xid)
554 break;
555 }
556 vcos_mutex_unlock(&st->wait_mtx);
557
558 if(i == ILCS_MAX_WAITING) {
559 // something bad happened, someone has sent a response back
560 // when the caller said they weren't expecting a response
561 vcos_assert(0);
562 return;
563 }
564
565 // check that we have enough space to copy into.
566 // if we haven't the user can tell by the updated rlen value.
567 if(len > *wait->rlen)
568 copy = *wait->rlen;
569
570 *wait->rlen = len;
571
572 // extract command from fifo and place in response buffer.
573 memcpy(wait->resp, msg, copy);
574
575 vcos_event_signal(&wait->event);
576}
577
578/* ----------------------------------------------------------------------
579 * helper function to transmit an ilcs command/response + payload
580 * -------------------------------------------------------------------- */
581static void ilcs_transmit(ILCS_SERVICE_T *st, uint32_t cmd, uint32_t xid,
582 const unsigned char *msg, int len,
583 const unsigned char *msg2, int len2)
584{
585 VCHIQ_ELEMENT_T vec[4];
586 int32_t count = 3;
587
588 vec[0].data = &cmd;
589 vec[0].size = sizeof(cmd);
590 vec[1].data = &xid;
591 vec[1].size = sizeof(xid);
592 vec[2].data = msg;
593 vec[2].size = len;
594
595 if(msg2)
596 {
597 vec[3].data = msg2;
598 vec[3].size = len2;
599 count++;
600 }
601
602#ifdef USE_VCHIQ_ARM
603 vchiq_queue_message(st->service, vec, count);
604#else
605 vchiq_queue_message(st->vchiq, st->fourcc, vec, count);
606#endif
607}
608
609/* ----------------------------------------------------------------------
610 * received response to an ILCS command
611 * -------------------------------------------------------------------- */
612static void
613ilcs_command(ILCS_SERVICE_T *st, uint32_t cmd, uint32_t xid, unsigned char *msg, int len)
614{
615 // execute this function call
616 unsigned char resp[VC_ILCS_MAX_CMD_LENGTH];
617 unsigned char *rbuf = resp;
618 int rlen = -1;
619 IL_FN_T fn;
620
621 if(cmd >= IL_FUNCTION_MAX_NUM) {
622 vcos_assert(0);
623 return;
624 }
625
626 fn = st->config.fns[cmd];
627 if(!fn) {
628 vcos_assert(0);
629 return;
630 }
631
632 // for one method we allow the response to go in the same slot as the
633 // msg, since it wants to return quite a big amount of debug information
634 // and we know this is safe.
635 if(cmd == IL_GET_DEBUG_INFORMATION)
636 {
637 int max = VCHIQ_SLOT_SIZE - 8;
638 IL_GET_DEBUG_INFORMATION_EXECUTE_T *exe = (IL_GET_DEBUG_INFORMATION_EXECUTE_T *) msg;
639 if(exe->len > max)
640 exe->len = max;
641
642 rbuf = msg;
643 }
644
645 // at this point we are executing in ILCS task context
646 // NOTE: this can cause ilcs_execute_function() calls from within guts of openmaxil!
647 fn(st->ilcs_common, msg, len, rbuf, &rlen);
648
649 // make sure rlen has been initialised by the function
650 vcos_assert(rlen != -1);
651
652 if(rlen > 0)
653 ilcs_transmit(st, IL_RESPONSE, xid, rbuf, rlen, NULL, 0);
654}
655
656/**
657 * send a string to the host side IL component service. if resp is NULL
658 * then there is no response to this call, so we should not wait for one.
659 *
660 * returns 0 on successful call made, -1 on failure to send call.
661 * on success, the response is written to 'resp' pointer
662 *
663 * @param data function parameter data
664 * @param len length of function parameter data
665 * @param data optional second function parameter data
666 * @param len2 length of second function parameter data
667 * @param msg_mem_handle option mem handle to be sent as part of msg data
668 * @param msg_offset Offset with msg mem handle
669 * @param msg_len Length of msg mem handle
670 * @param bulk_mem_handle Mem handle sent using VCHI bulk transfer
671 * @param bulk_offset Offset within memory handle
672 * @param bulk_len Length of bulk transfer
673 *
674 * -------------------------------------------------------------------- */
675
676static int ilcs_execute_function_ex(ILCS_SERVICE_T *st, IL_FUNCTION_T func,
677 void *data, int len,
678 void *data2, int len2,
679 VCHI_MEM_HANDLE_T bulk_mem_handle, void *bulk_offset, int bulk_len,
680 void *resp, int *rlen)
681{
682 ILCS_WAIT_T *wait = NULL;
683 int num = 0;
684 uint32_t xid;
685
686 if(st->kill_service)
687 return -1;
688
689 // need to atomically find free ->wait entry
690 vcos_mutex_lock(&st->wait_mtx);
691
692 // if resp is NULL, we do not expect any response
693 if(resp == NULL) {
694 xid = st->next_xid++;
695 }
696 else
697 {
698 int i;
699
700 if(st->timer_needed++ == 0)
701 {
702 vcos_timer_set(&st->timer, 10);
703 }
704
705 // we try a number of times then give up with an error message
706 // rather than just deadlocking
707
708 // Note: the real reason for the timeout is nothing to do with hardware
709 // errors, but is to ensure that if the ILCS thread is calling this function
710 // (because the client makes an OMX call from one of the callbacks) then
711 // the queue of messages from VideoCore still gets serviced.
712
713 for (i=0; i<ILCS_WAIT_TIMEOUT; i++) {
714 num = 0;
715
716 while(num < ILCS_MAX_WAITING && st->wait[num].resp != NULL)
717 num++;
718
719 if(num < ILCS_MAX_WAITING || i == ILCS_WAIT_TIMEOUT-1)
720 break;
721
722 // the previous time round this loop, we woke up because the timer
723 // expired, so restart it
724 if (st->timer_expired)
725 {
726 st->timer_expired = 0;
727 vcos_timer_set(&st->timer, 10);
728 }
729
730 // might be a fatal error if another thread is relying
731 // on this call completing before it can complete
732 // we'll pause until we can carry on and hope that's sufficient.
733 vcos_mutex_unlock(&st->wait_mtx);
734
735 // if we're the ilcs thread, then the waiters might need
736 // us to handle their response, so try and clear those now
737 if(vcos_thread_current() == &st->thread)
738 {
739 while (vcos_event_try(&st->wait_event) != VCOS_SUCCESS)
740 {
741 while(ilcs_process_message(st, 0))
742 if(st->kill_service >= CLOSED_CALLBACK)
743 return -1;
744 if (vcos_event_try(&st->wait_event) == VCOS_SUCCESS)
745 break;
746 vcos_sleep(1);
747 }
748 }
749 else
750 {
751 vcos_event_wait(&st->wait_event);
752 }
753
754 vcos_mutex_lock(&st->wait_mtx);
755 }
756
757 if(--st->timer_needed == 0)
758 {
759 vcos_timer_cancel(&st->timer);
760 st->timer_expired = 0;
761 }
762
763 if(num == ILCS_MAX_WAITING)
764 {
765 // failed to send message.
766 vcos_mutex_unlock(&st->wait_mtx);
767 return -1;
768 }
769
770 wait = &st->wait[num];
771
772 wait->resp = resp;
773 wait->rlen = rlen;
774 xid = wait->xid = st->next_xid++;
775 }
776
777 vcos_mutex_unlock(&st->wait_mtx);
778
779 if(bulk_len != 0)
780 vcos_semaphore_wait(&st->send_sem);
781
782 ilcs_transmit(st, func, xid, data, len, data2, len2);
783
784 if(bulk_len != 0)
785 {
786#ifdef USE_VCHIQ_ARM
787 vchiq_queue_bulk_transmit_handle(st->service, bulk_mem_handle, bulk_offset, bulk_len, NULL);
788#else
789 vchiq_queue_bulk_transmit(st->vchiq, st->fourcc, bulk_mem_handle, bulk_offset, bulk_len, NULL);
790#endif
791 vcos_semaphore_post(&st->send_sem);
792 }
793
794 if(!wait)
795 {
796 // nothing more to do
797 return 0;
798 }
799
800 if(vcos_thread_current() != &st->thread)
801 {
802 // block waiting for response
803 vcos_event_wait(&wait->event);
804 }
805 else
806 {
807 // since we're the server task, to receive our own response code
808 // we need to keep reading messages from the other side. In
809 // addition, our function executing on the host may also call
810 // functions on VideoCore before replying, so we need to handle
811 // all incoming messages until our response arrives.
812 for (;;)
813 {
814 // wait->sem will not be released until we process the response message
815 // so handle one incoming message
816 ilcs_process_message(st, 1);
817
818 // did the last message release wait->sem ?
819 if(st->kill_service >= CLOSED_CALLBACK || vcos_event_try(&wait->event) == VCOS_SUCCESS)
820 break;
821 }
822 }
823
824 // safe to do the following - the assignment of NULL is effectively atomic
825 wait->resp = NULL;
826 vcos_event_signal(&st->wait_event);
827
828 return st->kill_service >= CLOSED_CALLBACK ? -1 : 0;
829}
830
831int ilcs_execute_function(ILCS_SERVICE_T *st, IL_FUNCTION_T func, void *data, int len, void *resp, int *rlen)
832{
833 return ilcs_execute_function_ex(st, func, data, len, NULL, 0, VCHI_MEM_HANDLE_INVALID, 0, 0, resp, rlen);
834}
835
836/* ----------------------------------------------------------------------
837 * send a buffer via the IL component service.
838 * -------------------------------------------------------------------- */
839
840OMX_ERRORTYPE ilcs_pass_buffer(ILCS_SERVICE_T *st, IL_FUNCTION_T func, void *reference,
841 OMX_BUFFERHEADERTYPE *pBuffer)
842{
843 IL_PASS_BUFFER_EXECUTE_T exe;
844 IL_BUFFER_BULK_T fixup;
845 IL_RESPONSE_HEADER_T resp;
846 VCHI_MEM_HANDLE_T mem_handle = VCHI_MEM_HANDLE_INVALID;
847 void *ret = NULL, *data2 = NULL, *bulk_offset = NULL;
848 int len2 = 0, bulk_len = 0;
849 OMX_U8 *ptr = NULL;
850 int rlen = sizeof(resp);
851
852 if(st->kill_service)
853 return OMX_ErrorHardware;
854
855 if((func == IL_EMPTY_THIS_BUFFER && pBuffer->pInputPortPrivate == NULL) ||
856 (func == IL_FILL_THIS_BUFFER && pBuffer->pOutputPortPrivate == NULL))
857 {
858 // return this to pass conformance
859 // the actual error is using a buffer that hasn't be registered with usebuffer/allocatebuffer
860 return OMX_ErrorIncorrectStateOperation;
861 }
862
863 if((pBuffer->nFlags & OMX_BUFFERFLAG_EXTRADATA) || pBuffer->nFilledLen)
864 ptr = st->config.ilcs_mem_lock(pBuffer) + pBuffer->nOffset;
865
866 exe.reference = reference;
867 memcpy(&exe.bufferHeader, pBuffer, sizeof(OMX_BUFFERHEADERTYPE));
868
869 exe.bufferLen = pBuffer->nFilledLen;
870 if(pBuffer->nFlags & OMX_BUFFERFLAG_EXTRADATA)
871 {
872 // walk down extra-data's appended to the buffer data to work out their length
873 OMX_U8 *end = ptr + pBuffer->nAllocLen - pBuffer->nOffset;
874 OMX_OTHER_EXTRADATATYPE *extra =
875 (OMX_OTHER_EXTRADATATYPE *) (((uint32_t) (ptr + pBuffer->nFilledLen + 3)) & ~3);
876 OMX_BOOL b_corrupt = OMX_FALSE;
877 OMX_EXTRADATATYPE extra_type;
878
879 do
880 {
881 // sanity check the extra data before doing anything with it
882 if(((uint8_t *)extra) + sizeof(OMX_OTHER_EXTRADATATYPE) > end ||
883 ((uint8_t *)extra) + extra->nSize > end ||
884 extra->nSize < sizeof(OMX_OTHER_EXTRADATATYPE) ||
885 (extra->nSize & 3))
886 {
887 // shouldn't happen. probably a problem with the component
888 b_corrupt = OMX_TRUE;
889 break;
890 }
891
892 extra_type = extra->eType;
893 extra = (OMX_OTHER_EXTRADATATYPE *) (((uint8_t *) extra) + extra->nSize);
894 }
895 while(extra_type != OMX_ExtraDataNone);
896
897 // if corrupt then drop the extra data since we can't do anything with it
898 if(b_corrupt)
899 pBuffer->nFlags &= ~OMX_BUFFERFLAG_EXTRADATA;
900 else
901 exe.bufferLen = ((uint8_t *) extra) - ptr;
902 }
903
904 // check that the buffer fits in the allocated region
905 if(exe.bufferLen + pBuffer->nOffset > pBuffer->nAllocLen)
906 {
907 if(ptr != NULL)
908 st->config.ilcs_mem_unlock(pBuffer);
909
910 return OMX_ErrorBadParameter;
911 }
912
913 if(exe.bufferLen)
914 {
915 if(exe.bufferLen + sizeof(IL_PASS_BUFFER_EXECUTE_T) <= VC_ILCS_MAX_INLINE)
916 {
917 // Pass the data in the message itself, and avoid doing a bulk transfer at all...
918 exe.method = IL_BUFFER_INLINE;
919
920 data2 = ptr;
921 len2 = exe.bufferLen;
922 }
923 else
924 {
925 // Pass the misaligned area at the start at end inline within the
926 // message, and the bulk of the message using a separate bulk
927 // transfer
928 const uint8_t *start = ptr;
929 const uint8_t *end = start + exe.bufferLen;
930 const uint8_t *round_start = (const OMX_U8*)ILCS_ROUND_UP(start);
931 const uint8_t *round_end = (const OMX_U8*)ILCS_ROUND_DOWN(end);
932
933 exe.method = IL_BUFFER_BULK;
934
935 if(st->use_memmgr)
936 {
937 bulk_offset = (void *) (round_start-(ptr-pBuffer->nOffset));
938 mem_handle = (VCHI_MEM_HANDLE_T) pBuffer->pBuffer;
939 }
940 else
941 bulk_offset = (void *) round_start;
942
943 bulk_len = round_end-round_start;
944
945 if((fixup.headerlen = round_start - start) > 0)
946 memcpy(fixup.header, start, fixup.headerlen);
947
948 if((fixup.trailerlen = end - round_end) > 0)
949 memcpy(fixup.trailer, round_end, fixup.trailerlen);
950
951 data2 = &fixup;
952 len2 = sizeof(fixup);
953 }
954 }
955 else
956 {
957 exe.method = IL_BUFFER_NONE;
958 }
959
960 // when used for callbacks to client, no need for response
961 // so only set ret when use component to component
962 if(func == IL_EMPTY_THIS_BUFFER || func == IL_FILL_THIS_BUFFER)
963 ret = &resp;
964
965 if(ilcs_execute_function_ex(st, func, &exe, sizeof(IL_PASS_BUFFER_EXECUTE_T),
966 data2, len2, mem_handle, bulk_offset, bulk_len, ret, &rlen) < 0 || rlen != sizeof(resp))
967 {
968 ret = &resp;
969 resp.err = OMX_ErrorHardware;
970 }
971
972 if(ptr != NULL)
973 st->config.ilcs_mem_unlock(pBuffer);
974
975 return ret ? resp.err : OMX_ErrorNone;
976}
977
978
979/* ----------------------------------------------------------------------
980 * receive a buffer via the IL component service.
981 * -------------------------------------------------------------------- */
982
983OMX_BUFFERHEADERTYPE *ilcs_receive_buffer(ILCS_SERVICE_T *st, void *call, int clen, OMX_COMPONENTTYPE **pComp)
984{
985 IL_PASS_BUFFER_EXECUTE_T *exe = call;
986 OMX_BUFFERHEADERTYPE *pHeader = exe->bufferHeader.pInputPortPrivate;
987 OMX_U8 *dest, *pBuffer = pHeader->pBuffer;
988 OMX_PTR *pAppPrivate = pHeader->pAppPrivate;
989 OMX_PTR *pPlatformPrivate = pHeader->pPlatformPrivate;
990 OMX_PTR *pInputPortPrivate = pHeader->pInputPortPrivate;
991 OMX_PTR *pOutputPortPrivate = pHeader->pOutputPortPrivate;
992
993 if(st->kill_service)
994 return NULL;
995
996 vcos_assert(pHeader);
997 memcpy(pHeader, &exe->bufferHeader, sizeof(OMX_BUFFERHEADERTYPE));
998
999 *pComp = exe->reference;
1000
1001 pHeader->pBuffer = pBuffer;
1002 pHeader->pAppPrivate = pAppPrivate;
1003 pHeader->pPlatformPrivate = pPlatformPrivate;
1004 pHeader->pInputPortPrivate = pInputPortPrivate;
1005 pHeader->pOutputPortPrivate = pOutputPortPrivate;
1006
1007 dest = st->config.ilcs_mem_lock(pHeader) + pHeader->nOffset;
1008
1009 if(exe->method == IL_BUFFER_BULK)
1010 {
1011 IL_BUFFER_BULK_T *fixup = (IL_BUFFER_BULK_T *) (exe+1);
1012 VCHI_MEM_HANDLE_T mem_handle = VCHI_MEM_HANDLE_INVALID;
1013 void *bulk_offset;
1014 int32_t bulk_len = exe->bufferLen - fixup->headerlen - fixup->trailerlen;
1015
1016 vcos_assert(clen == sizeof(IL_PASS_BUFFER_EXECUTE_T) + sizeof(IL_BUFFER_BULK_T));
1017
1018 if(st->use_memmgr)
1019 {
1020 mem_handle = (VCHI_MEM_HANDLE_T) pBuffer;
1021 bulk_offset = (void*)(pHeader->nOffset + fixup->headerlen);
1022 }
1023 else
1024 bulk_offset = dest + fixup->headerlen;
1025
1026#ifdef USE_VCHIQ_ARM
1027 vchiq_queue_bulk_receive_handle(st->service, mem_handle, bulk_offset, bulk_len, NULL);
1028#else
1029 vchiq_queue_bulk_receive(st->vchiq, st->fourcc, mem_handle, bulk_offset, bulk_len, NULL);
1030#endif
1031
1032 vcos_event_wait(&st->bulk_rx);
1033
1034 if(st->kill_service)
1035 {
1036 // the bulk receive was aborted, and we're about the quit, however this function
1037 // being called means the buffer header control message made it across, so we
1038 // need to think that this buffer is on VideoCore. So pretend this is all okay,
1039 // but zero the buffer contents so we don't process bad data
1040 pHeader->nFilledLen = 0;
1041 pHeader->nFlags = 0;
1042 }
1043 else if(fixup->headerlen || fixup->trailerlen)
1044 {
1045 uint8_t *end = dest + exe->bufferLen;
1046
1047 if(fixup->headerlen)
1048 memcpy(dest, fixup->header, fixup->headerlen);
1049 if(fixup->trailerlen)
1050 memcpy(end-fixup->trailerlen, fixup->trailer, fixup->trailerlen);
1051 }
1052 }
1053 else if(exe->method == IL_BUFFER_INLINE)
1054 {
1055 IL_BUFFER_INLINE_T *buffer = (IL_BUFFER_INLINE_T *) (exe+1);
1056
1057 vcos_assert(clen == sizeof(IL_PASS_BUFFER_EXECUTE_T) + exe->bufferLen);
1058 memcpy(dest, buffer->buffer, exe->bufferLen);
1059 }
1060 else if(exe->method == IL_BUFFER_NONE)
1061 {
1062 vcos_assert(clen == sizeof(IL_PASS_BUFFER_EXECUTE_T));
1063 }
1064 else
1065 {
1066 vcos_assert(0);
1067 }
1068
1069 st->config.ilcs_mem_unlock(pHeader);
1070 return pHeader;
1071}
1072