| /* |
| * Copyright (c) 2019, The Linux Foundation. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * * Neither the name of The Linux Foundation nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| //#include "qurt_mutex.h" |
| #include <stdio.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <assert.h> |
| #include <pthread.h> |
| #include <dlfcn.h> |
| #include <stdlib.h> |
| #include <sys/eventfd.h> |
| |
| #include "platform_libs.h" |
| #include "HAP_farf.h" |
| #include "verify.h" |
| #include "mod_table.h" |
| #include "remote_priv.h" |
| #include "rpcmem.h" |
| #include "adsp_listener.h" |
| #include "listener_buf.h" |
| #include "shared.h" |
| #include "AEEstd.h" |
| #include "fastrpc_apps_user.h" |
| #include "AEEStdErr.h" |
| |
| #define LOGL(format, ...) VERIFY_PRINT_INFO(format, ##__VA_ARGS__) |
| #ifndef MALLOC |
| #define MALLOC malloc |
| #endif |
| |
| #ifndef CALLOC |
| #define CALLOC calloc |
| #endif |
| |
| #ifndef FREE |
| #define FREE free |
| #endif |
| |
| #ifndef REALLOC |
| #define REALLOC realloc |
| #endif |
| |
| #ifndef FREEIF |
| #define FREEIF(pv) \ |
| do {\ |
| if(pv) { \ |
| void* tmp = (void*)pv;\ |
| pv = 0;\ |
| FREE(tmp);\ |
| } \ |
| } while(0) |
| #endif |
| |
| #include <unistd.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <sys/eventfd.h> |
| |
| struct listener { |
| pthread_t thread; |
| int eventfd; |
| }; |
| |
| static struct listener linfo[NUM_DOMAINS_EXTEND] = |
| { [0 ... NUM_DOMAINS_EXTEND - 1] = { .thread = 0, .eventfd = -1 } }; |
| |
| //TODO: fix this to work over any number of buffers |
| // needs qaic to support extra buffers |
| #define MAX_BUFS 250 |
| struct invoke_bufs { |
| adsp_listener_buffer outbufs[MAX_BUFS]; |
| adsp_listener_buffer inbufs[MAX_BUFS]; |
| int inbufLenReqs[MAX_BUFS]; |
| int outbufLenReqs[MAX_BUFS]; |
| remote_arg args[2*MAX_BUFS]; |
| }; |
| |
| extern void set_thread_context(int domain); |
| |
| __QAIC_IMPL_EXPORT int __QAIC_IMPL(apps_remotectl_open)(const char* name, uint32* handle, char* dlStr, int dlerrorLen, int* dlErr) __QAIC_IMPL_ATTRIBUTE |
| { |
| return mod_table_open(name, handle, dlStr, dlerrorLen, dlErr); |
| } |
| |
| __QAIC_IMPL_EXPORT int __QAIC_IMPL(apps_remotectl_close)(uint32 handle, char* errStr, int errStrLen, int* dlErr) __QAIC_IMPL_ATTRIBUTE |
| { |
| return mod_table_close(handle, errStr, errStrLen, dlErr); |
| } |
| |
| #define RPC_FREEIF(heapid, buf) \ |
| do {\ |
| if(heapid == -1) {\ |
| FREEIF(buf);\ |
| } else {\ |
| if(buf) {\ |
| rpcmem_free_internal(buf);\ |
| buf = 0;\ |
| }\ |
| }\ |
| } while (0) |
| |
| static __inline void* rpcmem_realloc(int heapid, uint32 flags, void* buf, int oldsize, int size) { |
| if(heapid == -1) { |
| return REALLOC(buf, size); |
| } else { |
| void* bufnew = rpcmem_alloc_internal(heapid, flags, size); |
| if(buf && bufnew) { |
| memmove(bufnew, buf, oldsize); |
| rpcmem_free_internal(buf); |
| buf = NULL; |
| } |
| return bufnew; |
| } |
| } |
| |
| static void* listener(void* arg) { |
| struct listener* me = (struct listener*)arg; |
| int numOutBufs = 0; |
| int nErr = AEE_SUCCESS; |
| adsp_listener_invoke_ctx ctx = 0; |
| struct invoke_bufs* bufs = 0; |
| boolean bNeedMore; |
| int result = -1; |
| adsp_listener_remote_handle handle; |
| uint32 sc; |
| int ii, inBufsAllocated = 0; |
| const char* eheap = getenv("ADSP_LISTENER_HEAP_ID"); |
| int heapid = eheap == 0 ? 0 : (uint32)atoi(eheap); |
| const char* eflags = getenv("ADSP_LISTENER_HEAP_FLAGS"); |
| uint32 flags = eflags == 0 ? RPCMEM_HEAP_DEFAULT : (uint32)atoi(eflags); |
| |
| if(eheap || eflags) { |
| FARF(HIGH, "listener using ion heap: %d flags: %x\n", (int)heapid, (int)flags); |
| } |
| |
| VERIFYC(NULL != (bufs = rpcmem_realloc(heapid, flags, 0, 0, sizeof(*bufs))), AEE_ENORPCMEMORY); |
| memset(bufs, 0, sizeof(*bufs)); |
| set_thread_context((int)(me - &linfo[0])); |
| |
| do { |
| invoke: |
| bNeedMore = FALSE; |
| sc = 0xffffffff; |
| if(result != AEE_SUCCESS) { |
| numOutBufs = 0; |
| } |
| nErr = __QAIC_HEADER(adsp_listener_next_invoke)( |
| ctx, result, bufs->outbufs, numOutBufs, &ctx, |
| &handle, &sc, bufs->inbufs, inBufsAllocated, |
| bufs->inbufLenReqs, MAX_BUFS, bufs->outbufLenReqs, MAX_BUFS); |
| if(nErr) { |
| VERIFY_EPRINTF("listener protocol failure %x\n", nErr); |
| VERIFY(AEE_SUCCESS == (nErr = __QAIC_HEADER(adsp_listener_next_invoke)( |
| ctx, nErr, 0, 0, &ctx, |
| &handle, &sc, bufs->inbufs, inBufsAllocated, |
| bufs->inbufLenReqs, MAX_BUFS, bufs->outbufLenReqs, MAX_BUFS))); |
| } |
| |
| if(MAX_BUFS < REMOTE_SCALARS_INBUFS(sc) || MAX_BUFS < REMOTE_SCALARS_OUTBUFS(sc)) { |
| result = AEE_EMAXBUFS; |
| goto invoke; |
| } |
| for(ii = 0; ii < (int)REMOTE_SCALARS_INBUFS(sc); ++ii) { |
| if(bufs->inbufs[ii].dataLen < bufs->inbufLenReqs[ii]) { |
| if(0 != bufs->inbufLenReqs[ii]) { |
| bufs->inbufs[ii].data = rpcmem_realloc(heapid, flags, bufs->inbufs[ii].data, bufs->inbufs[ii].dataLen, bufs->inbufLenReqs[ii]); |
| if(0 == bufs->inbufs[ii].data) { |
| bufs->inbufs[ii].dataLen = 0; |
| result = AEE_ENORPCMEMORY; |
| goto invoke; |
| } |
| } |
| bufs->inbufs[ii].dataLen = bufs->inbufLenReqs[ii]; |
| inBufsAllocated = STD_MAX(inBufsAllocated, ii + 1); |
| bNeedMore = TRUE; |
| } |
| bufs->args[ii].buf.pv = bufs->inbufs[ii].data; |
| bufs->args[ii].buf.nLen = bufs->inbufLenReqs[ii]; |
| } |
| for(ii = 0; ii < (int)REMOTE_SCALARS_OUTBUFS(sc); ++ii) { |
| if(bufs->outbufs[ii].dataLen < bufs->outbufLenReqs[ii]) { |
| if(0 != bufs->outbufLenReqs[ii]) { |
| bufs->outbufs[ii].data = rpcmem_realloc(heapid, flags, bufs->outbufs[ii].data, bufs->outbufs[ii].dataLen, bufs->outbufLenReqs[ii]); |
| if(0 == bufs->outbufs[ii].data) { |
| result = AEE_ENORPCMEMORY; |
| goto invoke; |
| } |
| } |
| bufs->outbufs[ii].dataLen = bufs->outbufLenReqs[ii]; |
| } |
| bufs->args[ii + REMOTE_SCALARS_INBUFS(sc)].buf.pv = bufs->outbufs[ii].data; |
| bufs->args[ii + REMOTE_SCALARS_INBUFS(sc)].buf.nLen = bufs->outbufLenReqs[ii]; |
| } |
| numOutBufs = REMOTE_SCALARS_OUTBUFS(sc); |
| if(bNeedMore) { |
| assert(inBufsAllocated >= REMOTE_SCALARS_INBUFS(sc)); |
| if(0 != (result = __QAIC_HEADER(adsp_listener_invoke_get_in_bufs)(ctx, bufs->inbufs, |
| REMOTE_SCALARS_INBUFS(sc)))) { |
| FARF(HIGH, "adsp_listener_invoke_get_in_bufs failed %x\n", result); |
| goto invoke; |
| } |
| } |
| |
| result = mod_table_invoke(handle, sc, bufs->args); |
| } while(1); |
| bail: |
| for(ii = 0; ii < MAX_BUFS && bufs; ++ii) { |
| RPC_FREEIF(heapid, bufs->outbufs[ii].data); |
| RPC_FREEIF(heapid, bufs->inbufs[ii].data); |
| } |
| RPC_FREEIF(heapid, bufs); |
| if(nErr != AEE_SUCCESS) { |
| VERIFY_EPRINTF("Error %x: listener thread exiting\n", nErr); |
| } |
| return (void*)(uintptr_t)nErr; |
| } |
| |
| static int listener_start_thread(struct listener* me) { |
| return pthread_create(&me->thread, 0, listener, (void*)me); |
| } |
| #define MIN_BUF_SIZE 0x1000 |
| #define ALIGNB(sz) ((sz) == 0 ? MIN_BUF_SIZE : _SBUF_ALIGN((sz), MIN_BUF_SIZE)) |
| |
| static void* listener2(void* arg) { |
| struct listener* me = (struct listener*)arg; |
| int nErr = AEE_SUCCESS; |
| adsp_listener_invoke_ctx ctx = 0; |
| uint8* outBufs = 0; |
| int outBufsLen = 0, outBufsCapacity = 0; |
| uint8* inBufs = 0; |
| int inBufsLen = 0, inBufsLenReq = 0; |
| int result = -1; |
| adsp_listener_remote_handle handle = -1; |
| uint32 sc = 0; |
| const char* eheap = getenv("ADSP_LISTENER_HEAP_ID"); |
| int heapid = eheap == 0 ? -1 : atoi(eheap); |
| const char* eflags = getenv("ADSP_LISTENER_HEAP_FLAGS"); |
| uint32 flags = eflags == 0 ? 0 : (uint32)atoi(eflags); |
| const char* emin = getenv("ADSP_LISTENER_MEM_CACHE_SIZE"); |
| int cache_size = emin == 0 ? 0 : atoi(emin); |
| remote_arg args[512]; |
| struct sbuf buf; |
| eventfd_t event = 0xff; |
| |
| memset(args, 0, sizeof(args)); |
| set_thread_context((int)(me - &linfo[0])); |
| if(eheap || eflags || emin) { |
| FARF(HIGH, "listener using ion heap: %d flags: %x cache: %lld\n", (int)heapid, (int)flags, cache_size); |
| } |
| |
| do { |
| invoke: |
| sc = 0xffffffff; |
| if(result != 0) { |
| outBufsLen = 0; |
| } |
| FARF(HIGH, "responding message for %x %x %x %x", ctx, handle, sc, result); |
| nErr = __QAIC_HEADER(adsp_listener_next2)( |
| ctx, result, outBufs, outBufsLen, |
| &ctx, &handle, &sc, inBufs, inBufsLen, &inBufsLenReq); |
| FARF(HIGH, "got message for %x %x %x %x", ctx, handle, sc, nErr); |
| if(nErr) { |
| VERIFY_EPRINTF("listener protocol failure %x\n", nErr); |
| if (nErr == AEE_EINTERRUPTED) { |
| goto invoke; |
| } |
| VERIFY(0 == (nErr = __QAIC_HEADER(adsp_listener_next2)( |
| ctx, nErr, 0, 0, |
| &ctx, &handle, &sc, inBufs, inBufsLen, |
| &inBufsLenReq))); |
| } |
| if(ALIGNB(inBufsLenReq * 2) < inBufsLen && inBufsLen > cache_size) { |
| void* buf; |
| int size = ALIGNB(inBufsLenReq * 2); |
| if(NULL == (buf = rpcmem_realloc(heapid, flags, inBufs, inBufsLen, size))) { |
| result = AEE_ENORPCMEMORY; |
| FARF(HIGH, "rpcmem_realloc shrink failed"); |
| goto invoke; |
| } |
| inBufs = buf; |
| inBufsLen = size; |
| } |
| if(inBufsLenReq > inBufsLen) { |
| void* buf; |
| int req; |
| int oldLen = inBufsLen; |
| int size = _SBUF_ALIGN(inBufsLenReq, MIN_BUF_SIZE); |
| if(AEE_SUCCESS == (buf = rpcmem_realloc(heapid, flags, inBufs, inBufsLen, size))) { |
| result = AEE_ENORPCMEMORY; |
| FARF(ERROR, "rpcmem_realloc failed"); |
| goto invoke; |
| } |
| inBufs = buf; |
| inBufsLen = size; |
| if(0 != (result = __QAIC_HEADER(adsp_listener_get_in_bufs2)(ctx, oldLen, |
| inBufs + oldLen, |
| inBufsLen - oldLen, &req))) { |
| FARF(HIGH, "adsp_listener_invoke_get_in_bufs2 failed %x", result); |
| goto invoke; |
| } |
| if(req > inBufsLen) { |
| result = AEE_EBADSIZE; |
| FARF(HIGH, "adsp_listener_invoke_get_in_bufs2 failed %x", result); |
| goto invoke; |
| } |
| } |
| if(REMOTE_SCALARS_INHANDLES(sc) + REMOTE_SCALARS_OUTHANDLES(sc) > 0) { |
| result = AEE_EINVARGS; |
| goto invoke; |
| } |
| |
| sbuf_init(&buf, 0, inBufs, inBufsLen); |
| unpack_in_bufs(&buf, args, REMOTE_SCALARS_INBUFS(sc)); |
| unpack_out_lens(&buf, args + REMOTE_SCALARS_INBUFS(sc), REMOTE_SCALARS_OUTBUFS(sc)); |
| |
| sbuf_init(&buf, 0, 0, 0); |
| pack_out_bufs(&buf, args + REMOTE_SCALARS_INBUFS(sc), REMOTE_SCALARS_OUTBUFS(sc)); |
| outBufsLen = sbuf_needed(&buf); |
| |
| if(ALIGNB(outBufsLen*2) < outBufsCapacity && outBufsCapacity > cache_size) { |
| void* buf; |
| int size = ALIGNB(outBufsLen*2); |
| if(NULL == (buf = rpcmem_realloc(heapid, flags, outBufs, outBufsCapacity, size))) { |
| result = AEE_ENORPCMEMORY; |
| FARF(HIGH, "listener rpcmem_realloc shrink failed"); |
| goto invoke; |
| } |
| outBufs = buf; |
| outBufsCapacity = size; |
| } |
| if(outBufsLen > outBufsCapacity) { |
| void* buf; |
| int size = ALIGNB(outBufsLen); |
| if(NULL == (buf = rpcmem_realloc(heapid, flags, outBufs, outBufsCapacity, size))) { |
| result = AEE_ENORPCMEMORY; |
| FARF(ERROR, "listener rpcmem_realloc failed"); |
| goto invoke; |
| } |
| outBufs = buf; |
| outBufsLen = size; |
| outBufsCapacity = size; |
| } |
| sbuf_init(&buf, 0, outBufs, outBufsLen); |
| pack_out_bufs(&buf, args + REMOTE_SCALARS_INBUFS(sc), REMOTE_SCALARS_OUTBUFS(sc)); |
| |
| result = mod_table_invoke(handle, sc, args); |
| } while(1); |
| bail: |
| RPC_FREEIF(heapid, outBufs); |
| RPC_FREEIF(heapid, inBufs); |
| if(nErr != AEE_SUCCESS) { |
| VERIFY_EPRINTF("Error %x: listener thread exited", nErr); |
| } |
| eventfd_write(me->eventfd, event); |
| dlerror(); |
| return (void*)(uintptr_t)nErr; |
| } |
| static int listener_start_thread2(struct listener* me) { |
| return pthread_create(&me->thread, 0, listener2, (void*)me); |
| } |
| |
| extern int apps_remotectl_skel_invoke(uint32 _sc, remote_arg* _pra); |
| extern int apps_std_skel_invoke(uint32 _sc, remote_arg* _pra); |
| extern int apps_mem_skel_invoke(uint32 _sc, remote_arg* _pra); |
| extern int adspmsgd_apps_skel_invoke(uint32_t _sc, remote_arg* _pra); |
| |
| #include "adsp_listener_stub.c" |
| PL_DEP(mod_table) |
| PL_DEP(apps_std); |
| |
| void listener_android_deinit(void) { |
| PL_DEINIT(mod_table); |
| PL_DEINIT(apps_std); |
| } |
| |
| int listener_android_init(void) { |
| int nErr = 0; |
| |
| VERIFY(AEE_SUCCESS == (nErr = PL_INIT(mod_table))); |
| VERIFY(AEE_SUCCESS == (nErr = PL_INIT(apps_std))); |
| VERIFY(AEE_SUCCESS == (nErr = mod_table_register_const_handle(0, "apps_remotectl", apps_remotectl_skel_invoke))); |
| VERIFY(AEE_SUCCESS == (nErr = mod_table_register_static("apps_std", apps_std_skel_invoke))); |
| VERIFY(AEE_SUCCESS == (nErr = mod_table_register_static("apps_mem", apps_mem_skel_invoke))); |
| VERIFY(AEE_SUCCESS == (nErr = mod_table_register_static("adspmsgd_apps", adspmsgd_apps_skel_invoke))); |
| bail: |
| if(nErr != AEE_SUCCESS) { |
| listener_android_deinit(); |
| VERIFY_EPRINTF("Error %x: fastrpc listener initialization error", nErr); |
| } |
| return nErr; |
| } |
| |
| void listener_android_domain_deinit(int domain) { |
| struct listener* me = &linfo[domain]; |
| |
| FARF(HIGH, "fastrpc listener joining to exit"); |
| if(me->thread) { |
| pthread_join(me->thread, 0); |
| me->thread = 0; |
| } |
| FARF(HIGH, "fastrpc listener joined"); |
| if(me->eventfd != -1) { |
| close(me->eventfd); |
| me->eventfd = -1; |
| } |
| } |
| |
| int listener_android_domain_init(int domain) { |
| struct listener* me = &linfo[domain]; |
| int nErr = 0; |
| |
| VERIFYC(-1 != (me->eventfd = eventfd(0, 0)), AEE_EINVALIDFD); |
| nErr = __QAIC_HEADER(adsp_listener_init2)(); |
| if(AEE_EUNSUPPORTEDAPI == nErr) { |
| FARF(HIGH, "listener2 initialization error falling back to listener1 %x", nErr); |
| VERIFY(AEE_SUCCESS == (nErr = __QAIC_HEADER(adsp_listener_init)())); |
| VERIFY(AEE_SUCCESS == (nErr = listener_start_thread(me))); |
| } else if(AEE_SUCCESS == nErr) { |
| FARF(HIGH, "listener2 initialized for domain %d", domain); |
| VERIFY(AEE_SUCCESS == (nErr = listener_start_thread2(me))); |
| } |
| |
| bail: |
| if(nErr != AEE_SUCCESS) { |
| VERIFY_EPRINTF("Error %x: listener android domain init failed. domain %d\n", nErr, domain); |
| listener_android_domain_deinit(domain); |
| } |
| return nErr; |
| } |
| |
| int listener_android_geteventfd(int domain, int *fd) { |
| struct listener* me = &linfo[domain]; |
| int nErr = 0; |
| |
| VERIFYC(-1 != me->eventfd, AEE_EINVALIDFD); |
| *fd = me->eventfd; |
| bail: |
| if (nErr != AEE_SUCCESS) { |
| VERIFY_EPRINTF("Error %x: listener android getevent file descriptor failed for domain %d\n", nErr, domain); |
| } |
| return nErr; |
| } |
| |
| PL_DEFINE(listener_android, listener_android_init, listener_android_deinit) |