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 <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 /******************************************************************************
47 Private 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 
65 typedef struct {
66    int xid;
67    void *resp;
68    int *rlen;
69    VCOS_EVENT_T event;
70 } ILCS_WAIT_T;
71 
72 typedef 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 
79 struct 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 /******************************************************************************
115 Private functions in this file.
116 Define as static.
117 ******************************************************************************/
118 #ifdef USE_VCHIQ_ARM
119 static VCHIQ_STATUS_T ilcs_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, VCHIQ_SERVICE_HANDLE_T service_user, void *bulk_user);
120 #else
121 static int ilcs_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, void *service_user, void *bulk_user);
122 #endif
123 static void *ilcs_task(void *param);
124 static void ilcs_response(ILCS_SERVICE_T *st, uint32_t xid, const unsigned char *msg, int len );
125 static 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);
128 static void ilcs_command(ILCS_SERVICE_T *st, uint32_t cmd, uint32_t xid, unsigned char *msg, int len);
129 static void ilcs_timer(void *param);
130 static int ilcs_process_message(ILCS_SERVICE_T *st, int block);
131 
132 /* ----------------------------------------------------------------------
133  * initialise OpenMAX IL component service
134  * -------------------------------------------------------------------- */
135 #ifdef USE_VCHIQ_ARM
ilcs_init(VCHIQ_INSTANCE_T state,void ** connection,ILCS_CONFIG_T * config,int use_memmgr)136 ILCS_SERVICE_T *ilcs_init(VCHIQ_INSTANCE_T state, void **connection, ILCS_CONFIG_T *config, int use_memmgr)
137 #else
138 ILCS_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  * -------------------------------------------------------------------- */
ilcs_send_quit(ILCS_SERVICE_T * st)272 static 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  * -------------------------------------------------------------------- */
ilcs_deinit(ILCS_SERVICE_T * st)300 void 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  * -------------------------------------------------------------------- */
ilcs_timer(void * param)312 static 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  * -------------------------------------------------------------------- */
ilcs_get_common(ILCS_SERVICE_T * st)324 ILCS_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  * -------------------------------------------------------------------- */
ilcs_thread_current(void * param)333 int 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
ilcs_callback(VCHIQ_REASON_T reason,VCHIQ_HEADER_T * header,VCHIQ_SERVICE_HANDLE_T service_user,void * bulk_user)344 static 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
348 static 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  * -------------------------------------------------------------------- */
426 static 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 
475 static 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  * -------------------------------------------------------------------- */
544 static 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  * -------------------------------------------------------------------- */
581 static 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  * -------------------------------------------------------------------- */
612 static void
613 ilcs_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 
676 static 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 
831 int 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 
840 OMX_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  * receive a buffer via the IL component service.
980  * -------------------------------------------------------------------- */
981 
982 OMX_BUFFERHEADERTYPE *ilcs_receive_buffer(ILCS_SERVICE_T *st, void *call, int clen, OMX_COMPONENTTYPE **pComp)
983 {
984    IL_PASS_BUFFER_EXECUTE_T *exe = call;
985    OMX_BUFFERHEADERTYPE *pHeader = exe->bufferHeader.pInputPortPrivate;
986    OMX_U8 *dest, *pBuffer = pHeader->pBuffer;
987    OMX_PTR *pAppPrivate = pHeader->pAppPrivate;
988    OMX_PTR *pPlatformPrivate = pHeader->pPlatformPrivate;
989    OMX_PTR *pInputPortPrivate = pHeader->pInputPortPrivate;
990    OMX_PTR *pOutputPortPrivate = pHeader->pOutputPortPrivate;
991 
992    if(st->kill_service)
993       return NULL;
994 
995    vcos_assert(pHeader);
996    memcpy(pHeader, &exe->bufferHeader, sizeof(OMX_BUFFERHEADERTYPE));
997 
998    *pComp = exe->reference;
999 
1000    pHeader->pBuffer = pBuffer;
1001    pHeader->pAppPrivate = pAppPrivate;
1002    pHeader->pPlatformPrivate = pPlatformPrivate;
1003    pHeader->pInputPortPrivate = pInputPortPrivate;
1004    pHeader->pOutputPortPrivate = pOutputPortPrivate;
1005 
1006    dest = st->config.ilcs_mem_lock(pHeader) + pHeader->nOffset;
1007 
1008    if(exe->method == IL_BUFFER_BULK)
1009    {
1010       IL_BUFFER_BULK_T *fixup = (IL_BUFFER_BULK_T *) (exe+1);
1011       VCHI_MEM_HANDLE_T mem_handle = VCHI_MEM_HANDLE_INVALID;
1012       void *bulk_offset;
1013       int32_t bulk_len = exe->bufferLen - fixup->headerlen - fixup->trailerlen;
1014 
1015       vcos_assert(clen == sizeof(IL_PASS_BUFFER_EXECUTE_T) + sizeof(IL_BUFFER_BULK_T));
1016 
1017       if(st->use_memmgr)
1018       {
1019          mem_handle = (VCHI_MEM_HANDLE_T) pBuffer;
1020          bulk_offset = (void*)(pHeader->nOffset + fixup->headerlen);
1021       }
1022       else
1023          bulk_offset = dest + fixup->headerlen;
1024 
1025 #ifdef USE_VCHIQ_ARM
1026       vchiq_queue_bulk_receive_handle(st->service, mem_handle, bulk_offset, bulk_len, NULL);
1027 #else
1028       vchiq_queue_bulk_receive(st->vchiq, st->fourcc, mem_handle, bulk_offset, bulk_len, NULL);
1029 #endif
1030 
1031       vcos_event_wait(&st->bulk_rx);
1032 
1033       if(st->kill_service)
1034       {
1035          // the bulk receive was aborted, and we're about the quit, however this function
1036          // being called means the buffer header control message made it across, so we
1037          // need to think that this buffer is on VideoCore.  So pretend this is all okay,
1038          // but zero the buffer contents so we don't process bad data
1039          pHeader->nFilledLen = 0;
1040          pHeader->nFlags = 0;
1041       }
1042       else if(fixup->headerlen || fixup->trailerlen)
1043       {
1044          uint8_t *end = dest + exe->bufferLen;
1045 
1046          if(fixup->headerlen)
1047             memcpy(dest, fixup->header, fixup->headerlen);
1048          if(fixup->trailerlen)
1049             memcpy(end-fixup->trailerlen, fixup->trailer, fixup->trailerlen);
1050       }
1051    }
1052    else if(exe->method == IL_BUFFER_INLINE)
1053    {
1054       IL_BUFFER_INLINE_T *buffer = (IL_BUFFER_INLINE_T *) (exe+1);
1055 
1056       vcos_assert(clen == sizeof(IL_PASS_BUFFER_EXECUTE_T) + exe->bufferLen);
1057       memcpy(dest, buffer->buffer, exe->bufferLen);
1058    }
1059    else if(exe->method == IL_BUFFER_NONE)
1060    {
1061       vcos_assert(clen == sizeof(IL_PASS_BUFFER_EXECUTE_T));
1062    }
1063    else
1064    {
1065       vcos_assert(0);
1066    }
1067 
1068    st->config.ilcs_mem_unlock(pHeader);
1069    return pHeader;
1070 }
1071