From a7d41a6fa1e29d800ce8ac9e95e8f943814463e8 Mon Sep 17 00:00:00 2001 From: Fulup Ar Foll Date: Fri, 18 Aug 2017 01:09:56 +0200 Subject: Integration with Alsa HookPlugin is now working. --- Alsa-Plugin/Alsa-Policy-Hook/PolicyHookCb.c | 43 +- Alsa-Plugin/Alsa-Policy-Hook/README.md | 7 +- Alsa-afb/Alsa-ApiHat.c | 6 +- Audio-Common/filescan-utils.c | 4 +- Audio-Common/filescan-utils.h | 2 +- Controler-afb/CMakeLists.txt | 84 -- Controler-afb/README.md | 254 ----- Controler-afb/ctl-apidef.h | 129 --- Controler-afb/ctl-apidef.json | 281 ------ Controler-afb/ctl-binding.c | 65 -- Controler-afb/ctl-binding.h | 137 --- Controler-afb/ctl-dispatch.c | 643 ------------ Controler-afb/ctl-lua.c | 1026 ------------------- Controler-afb/ctl-plugin-sample.c | 134 --- Controler-afb/ctl-policy.c | 221 ----- Controler-afb/ctl-timer.c | 156 --- Controller-afb/CMakeLists.txt | 84 ++ Controller-afb/README.md | 333 +++++++ Controller-afb/a.out | Bin 0 -> 12552 bytes Controller-afb/ctl-apidef.h | 115 +++ Controller-afb/ctl-apidef.json | 250 +++++ Controller-afb/ctl-binding.c | 65 ++ Controller-afb/ctl-binding.h | 146 +++ Controller-afb/ctl-dispatch.c | 665 +++++++++++++ Controller-afb/ctl-lua.c | 1033 ++++++++++++++++++++ Controller-afb/ctl-plugin-sample.c | 134 +++ Controller-afb/ctl-policy.c | 221 +++++ Controller-afb/ctl-timer.c | 101 ++ HAL-afb/HAL-interface/hal-interface.c | 13 +- conf.d/alsa-sample/asoundrc.sample | 160 --- conf.d/alsa-sample/ucm.sample/HDA Intel PCH.conf | 6 - conf.d/alsa-sample/ucm.sample/HiFi.conf | 84 -- conf.d/alsa-sample/ucm.sample/README | 2 - conf.d/cmake/config.cmake | 8 +- conf.d/project/alsa.d/asoundrc.sample | 146 +++ .../project/alsa.d/ucm.sample/HDA Intel PCH.conf | 6 + conf.d/project/alsa.d/ucm.sample/HiFi.conf | 84 ++ conf.d/project/alsa.d/ucm.sample/README | 2 + conf.d/project/config.d/onload-audio-control.json | 14 +- conf.d/project/lua.d/doscript-helloworld.lua | 2 +- conf.d/project/lua.d/onload-audio-0utils.lua | 86 ++ conf.d/project/lua.d/onload-audio-controls.lua | 159 ++- conf.d/project/lua.d/onload-audio-oncall.lua | 16 +- conf.d/project/lua.d/onload-audio-timer.lua | 10 +- conf.d/project/lua.d/onload-audio-utils.lua | 32 - data/default-control-policy.json | 2 +- htdocs/AudioBinding.js | 1 - htdocs/alsa-hal.html | 16 +- htdocs/audio-control.html | 23 +- nbproject/configurations.xml | 933 +++++++++++++++--- nbproject/project.xml | 4 + 51 files changed, 4509 insertions(+), 3639 deletions(-) delete mode 100644 Controler-afb/CMakeLists.txt delete mode 100644 Controler-afb/README.md delete mode 100644 Controler-afb/ctl-apidef.h delete mode 100644 Controler-afb/ctl-apidef.json delete mode 100644 Controler-afb/ctl-binding.c delete mode 100644 Controler-afb/ctl-binding.h delete mode 100644 Controler-afb/ctl-dispatch.c delete mode 100644 Controler-afb/ctl-lua.c delete mode 100644 Controler-afb/ctl-plugin-sample.c delete mode 100644 Controler-afb/ctl-policy.c delete mode 100644 Controler-afb/ctl-timer.c create mode 100644 Controller-afb/CMakeLists.txt create mode 100644 Controller-afb/README.md create mode 100755 Controller-afb/a.out create mode 100644 Controller-afb/ctl-apidef.h create mode 100644 Controller-afb/ctl-apidef.json create mode 100644 Controller-afb/ctl-binding.c create mode 100644 Controller-afb/ctl-binding.h create mode 100644 Controller-afb/ctl-dispatch.c create mode 100644 Controller-afb/ctl-lua.c create mode 100644 Controller-afb/ctl-plugin-sample.c create mode 100644 Controller-afb/ctl-policy.c create mode 100644 Controller-afb/ctl-timer.c delete mode 100644 conf.d/alsa-sample/asoundrc.sample delete mode 100644 conf.d/alsa-sample/ucm.sample/HDA Intel PCH.conf delete mode 100644 conf.d/alsa-sample/ucm.sample/HiFi.conf delete mode 100644 conf.d/alsa-sample/ucm.sample/README create mode 100644 conf.d/project/alsa.d/asoundrc.sample create mode 100644 conf.d/project/alsa.d/ucm.sample/HDA Intel PCH.conf create mode 100644 conf.d/project/alsa.d/ucm.sample/HiFi.conf create mode 100644 conf.d/project/alsa.d/ucm.sample/README create mode 100644 conf.d/project/lua.d/onload-audio-0utils.lua delete mode 100644 conf.d/project/lua.d/onload-audio-utils.lua diff --git a/Alsa-Plugin/Alsa-Policy-Hook/PolicyHookCb.c b/Alsa-Plugin/Alsa-Policy-Hook/PolicyHookCb.c index 7c85e22..80a9ceb 100644 --- a/Alsa-Plugin/Alsa-Policy-Hook/PolicyHookCb.c +++ b/Alsa-Plugin/Alsa-Policy-Hook/PolicyHookCb.c @@ -53,11 +53,14 @@ #define MAX_EVT_CALL 10 // timeout in ms -#define REQUEST_DEFAULT_TIMEOUT 100 +#define REQUEST_DEFAULT_TIMEOUT 500 #ifndef MAINLOOP_WATCHDOG -#define MAINLOOP_WATCHDOG 60000 +#define MAINLOOP_WATCHDOG 100000 #endif +// closing message is added to query when PCM is closed +#define CLOSING_MSG ",\"source\":-1}" + // Currently not implemented #define UNUSED_ARG(x) UNUSED_ ## x __attribute__((__unused__)) void OnRequestCB(void* UNUSED_ARG(handle) , const char* UNUSED_ARG(api), const char* UNUSED_ARG(verb), struct afb_wsj1_msg*UNUSED_ARG(msg)) {} @@ -67,6 +70,7 @@ typedef struct { const char *verb; long timeout; char *query; + size_t length; sd_event_source *evtSource; char *callIdTag; @@ -101,7 +105,7 @@ static void *LoopInThread(void *handle) { /* loop until end */ for (;;) { - if (afbClient->verbose) printf("ON-MAINLOOP Active count=%d\n", count++); + if (afbClient->verbose) printf("ON-MAINLOOP ping=%d\n", count++); sd_event_run(afbClient->sdLoop, watchdog); } @@ -214,7 +218,6 @@ int OnTimeoutCB (sd_event_source* source, uint64_t timer, void* handle) { static int CallWithTimeout(afbClientT *afbClient, afbRequestT *afbRequest, int count, hookActionT action) { uint64_t usec; int err; - const char *query; // create a unique tag for request (void) asprintf(&afbRequest->callIdTag, "%d:%s/%s", count, afbRequest->api, afbRequest->verb); @@ -223,9 +226,19 @@ static int CallWithTimeout(afbClientT *afbClient, afbRequestT *afbRequest, int c sd_event_now(afbClient->sdLoop, CLOCK_MONOTONIC, &usec); sd_event_add_time(afbClient->sdLoop, &afbRequest->evtSource, CLOCK_MONOTONIC, usec+afbRequest->timeout*1000, 250, OnTimeoutCB, afbClient); - if (action == HOOK_INSTALL) query=afbRequest->query; - else query="{'closing': 1}"; - err = afb_wsj1_call_s(afbClient->wsj1, afbRequest->api, afbRequest->verb, query, OnResponseCB, afbRequest); + if (afbClient->verbose) printf("CALL-REQUEST api=%s/%s tag=%s\n", afbRequest->api, afbRequest->verb, afbRequest->callIdTag); + + // on PCM close replace last '}' by CLOSING_MSG + if (action == HOOK_CLOSE) { + for (size_t index=afbRequest->length; index >0; index--) { + if (afbRequest->query[index] == '}') { + strcpy (&afbRequest->query[index], CLOSING_MSG); + break; + } + } + } + + err = afb_wsj1_call_s(afbClient->wsj1, afbRequest->api, afbRequest->verb, afbRequest->query, OnResponseCB, afbRequest); if (err) goto OnErrorExit; // save client handle in request @@ -421,18 +434,22 @@ int PLUGIN_ENTRY_POINT (snd_pcm_t *pcm, snd_config_t *conf) { if (!err) { const char *query; if (snd_config_get_string(itemConf, &query) < 0) { - SNDERR("Invalid query string %s", id); + SNDERR("Invalid args string %s", id); goto OnErrorExit; } + // reserve enough space to ad closing message + afbRequest[callCount]->length= strlen(query); + afbRequest[callCount]->query = malloc (afbRequest[callCount]->length+strlen(CLOSING_MSG)+1); + strcpy (afbRequest[callCount]->query, query); + // cleanup string for json_tokener - afbRequest[callCount]->query = strdup(query); for (int idx = 0; query[idx] != '\0'; idx++) { if (query[idx] == '\'') afbRequest[callCount]->query[idx] = '"'; else afbRequest[callCount]->query[idx] = query[idx]; } - json_object *queryJ = json_tokener_parse(query); + json_object *queryJ = json_tokener_parse(afbRequest[callCount]->query); if (!queryJ) { - SNDERR("Invalid Json %s query=%s format \"{'tok1':'val1', 'tok2':'val2'}\" ", id, query); + SNDERR("Invalid Json %s args=%s should be args=\"{'tok1':'val1', 'tok2':'val2'}\" ", id, afbRequest[callCount]->query); goto OnErrorExit; } } @@ -520,7 +537,7 @@ int PLUGIN_ENTRY_POINT (snd_pcm_t *pcm, snd_config_t *conf) { // wait for all call request to return sem_wait(&afbClient->semaphore); if (afbClient->error) { - fprintf (stderr, "PCM Authorisation from Audio Agent Refused\n"); + fprintf (stderr, "PCM Authorisation Deny from AAAA Controller (AGL Advanced Audio Agent)\n"); goto OnErrorExit; } @@ -528,7 +545,7 @@ int PLUGIN_ENTRY_POINT (snd_pcm_t *pcm, snd_config_t *conf) { return 0; OnErrorExit: - fprintf(stderr, "\nAlsaPcmHook Plugin Install Fail PCM=%s\n", snd_pcm_name(afbClient->pcm)); + fprintf(stderr, "\nAlsaPcmHook Plugin Install Fail PCM=%s\n", snd_pcm_name(pcm)); if (h_close) snd_pcm_hook_remove(h_close); diff --git a/Alsa-Plugin/Alsa-Policy-Hook/README.md b/Alsa-Plugin/Alsa-Policy-Hook/README.md index 8fa2643..6a8e67e 100644 --- a/Alsa-Plugin/Alsa-Policy-Hook/README.md +++ b/Alsa-Plugin/Alsa-Policy-Hook/README.md @@ -14,6 +14,10 @@ Installation - This plugin implement a hook on a slave PCM. Typically this slave PCM is a dedicated virtual channel (eg: navigation, emergency,...) - Config should be place in ~/.asoundrc (see config sample in PROJECT_ROOT/conf.d/alsa) +Test + - Install a full .asoundrc from conf.d/project/alsa.d + - speaker-test -DMyNavigationHook -c2 -twav + Config ``` # Define sharelib location and entry point @@ -42,8 +46,7 @@ pcm.MyNavigationHook { navigation-ctl { api "control" verb "dispatch" - target "navigation" - args "{'any_valid': 'json object'}" + args "{'target':'navigation', 'args':{'device':'Jabra SOLEMATE v1.34.0'}}" } # subscribe to Audio Agent Event map them to signal subscribe-evt { diff --git a/Alsa-afb/Alsa-ApiHat.c b/Alsa-afb/Alsa-ApiHat.c index 1e9f5ad..a8663ca 100644 --- a/Alsa-afb/Alsa-ApiHat.c +++ b/Alsa-afb/Alsa-ApiHat.c @@ -31,7 +31,7 @@ #include "Alsa-ApiHat.h" STATIC int AlsaInit(void) { - int rc= prctl(PR_SET_NAME, "afb-audio-agent\0",NULL,NULL,NULL); + int rc= prctl(PR_SET_NAME, "afb-audio-agent",NULL,NULL,NULL); if (rc) AFB_ERROR("ERROR: AlsaCore fail to rename process"); return rc; @@ -62,8 +62,8 @@ static const struct afb_verb_v2 api_verbs[] = { /* * description of the binding for afb-daemon */ -const struct afb_binding_v2 afbBindingV2 = { +const afb_binding_v2 afbBindingV2 = { .api = "alsacore", .verbs = api_verbs, - .init = AlsaInit, + .preinit = AlsaInit, }; diff --git a/Audio-Common/filescan-utils.c b/Audio-Common/filescan-utils.c index 33dacdf..af0b5c9 100644 --- a/Audio-Common/filescan-utils.c +++ b/Audio-Common/filescan-utils.c @@ -27,7 +27,7 @@ // List Avaliable Configuration Files -PUBLIC json_object* ScanForConfig (char* searchPath, CtlScanDirModeT mode, const char *pre, const char *ext) { +PUBLIC json_object* ScanForConfig (const char* searchPath, CtlScanDirModeT mode, const char *pre, const char *ext) { json_object *responseJ; char *dirPath; char* dirList= strdup(searchPath); @@ -94,7 +94,7 @@ PUBLIC const char *GetMidleName(const char*name) { int start; if (fullname[idx] == '-') { start = idx + 1; - for (int jdx = start; fullname[jdx] != '\0'; jdx++) { + for (int jdx = start; ; jdx++) { if (fullname[jdx] == '-' || fullname[jdx] == '.' || fullname[jdx] == '\0') { fullname[jdx] = '\0'; return &fullname[start]; diff --git a/Audio-Common/filescan-utils.h b/Audio-Common/filescan-utils.h index 671e728..cbe15de 100644 --- a/Audio-Common/filescan-utils.h +++ b/Audio-Common/filescan-utils.h @@ -35,7 +35,7 @@ typedef enum { PUBLIC const char *GetMidleName(const char*name); PUBLIC const char *GetBinderName(); -PUBLIC json_object* ScanForConfig (char* searchPath, CtlScanDirModeT mode, const char *pre, const char *ext); +PUBLIC json_object* ScanForConfig (const char* searchPath, CtlScanDirModeT mode, const char *pre, const char *ext); #endif /* FILESCAN_UTILS_H */ diff --git a/Controler-afb/CMakeLists.txt b/Controler-afb/CMakeLists.txt deleted file mode 100644 index a9e8e60..0000000 --- a/Controler-afb/CMakeLists.txt +++ /dev/null @@ -1,84 +0,0 @@ -########################################################################### -# Copyright 2015, 2016, 2017 IoT.bzh -# -# author: Fulup Ar Foll -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -########################################################################### - - -# Generate API-v2 hat from OpenAPI json definition -macro(SET_TARGET_GENSKEL TARGET_NAME API_DEF_NAME) - add_custom_command(OUTPUT ${API_DEF_NAME}.h - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS ${API_DEF_NAME}.json - COMMAND afb-genskel ${API_DEF_NAME}.json >${API_DEF_NAME}.h - ) - add_custom_target(${API_DEF_NAME}_OPENAPI DEPENDS ${API_DEF_NAME}.h) - add_dependencies(${TARGET_NAME} ${API_DEF_NAME}_OPENAPI) -endmacro(SET_TARGET_GENSKEL) - -# Include LUA only when requested -if(CONTROL_SUPPORT_LUA) - message(STATUS "Notice: LUA Controler Support Selected") - set(CTL_LUA_SOURCE ctl-lua.c) -endif(CONTROL_SUPPORT_LUA) - -# Add target to project dependency list -PROJECT_TARGET_ADD(control-afb) - - # Define project Targets - ADD_LIBRARY(${TARGET_NAME} MODULE ctl-binding.c ctl-timer.c ctl-dispatch.c ${CTL_LUA_SOURCE}) - - # Generate API-v2 hat from OpenAPI json definition - SET_TARGET_GENSKEL(${TARGET_NAME} ctl-apidef) - - # Binder exposes a unique public entry point - SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES - PREFIX "afb-" - LABELS "BINDING" - LINK_FLAGS ${BINDINGS_LINK_FLAG} - OUTPUT_NAME ${TARGET_NAME} - ) - - # Library dependencies (include updates automatically) - TARGET_LINK_LIBRARIES(${TARGET_NAME} - audio-common - ${link_libraries} - ) - - # installation directory - INSTALL(TARGETS ${TARGET_NAME} - LIBRARY DESTINATION CMAKE_INSTALL_PREFIX ) - -PROJECT_TARGET_ADD(audio-plugin-sample) - - # Define targets - ADD_LIBRARY(${TARGET_NAME} MODULE ctl-plugin-sample.c) - - # Alsa Plugin properties - SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES - PREFIX ${CTL_PLUGIN_PRE} - SUFFIX ${CTL_PLUGIN_EXT} - OUTPUT_NAME ${TARGET_NAME} - ) - - # Library dependencies (include updates automatically) - TARGET_LINK_LIBRARIES(${TARGET_NAME} - audio-common - ${link_libraries} - ) - - # installation directory - INSTALL(TARGETS ${TARGET_NAME} - LIBRARY DESTINATION ${BINDINGS_INSTALL_DIR}/controler) diff --git a/Controler-afb/README.md b/Controler-afb/README.md deleted file mode 100644 index a81d4f7..0000000 --- a/Controler-afb/README.md +++ /dev/null @@ -1,254 +0,0 @@ -Controler AAAA(AGL Advance Audio Controler) and more. ------------------------------------------------------------- - - * Object: Generic Controler to handle Policy,Small Business Logic, Glue in between components, ... - * Status: Release Candidate - * Author: Fulup Ar Foll fulup@iot.bzh - * Date : August-2017 - -## Functionalities: - - Create an application dedicate controller from a JSON config file - - Each controls (eg: navigation, multimedia, ...) is a suite of actions. When all actions succeed control is granted, if one fail control acces is denied. - - Actions can either be: - + Invocation to an other binding API, either internal or external (eg: a policy service, Alsa UCM, ...) - + C routines from a user provider plugin (eg: policy routine, proprietary code, ...) - + Lua script function. Lua provides access to every AGL appfw functionalities and can be extended from C user provided plugins. - -## Installation - - Controler is a native part of AGL Advance Audio Framework but may be used independently with any other service or application binder. - - Dependencies: the only dependencies are audio-common for JSON-WRAP and Filescan-utils capabilities. - - Controler relies on Lua-5.3, when not needed Lua might be removed at compilation time. - -## Config - -Configuration is loaded dynamically during startup time. The controller scans CONTROL_CONFIG_PATH for a file corresponding to pattern -"onload-bindername-xxxxx.json". When controller runs within AAAA binder it searches for "onload-audio-xxxx.json". First file found in the path the loaded -any other files corresponding to the same pather are ignored and only generate a warning. - -Each bloc in the configuration file are defined with - * label: must be provided is used either for debugging or as input for the action (eg: signal name, control name, ...) - * info: optional used for documentation purpose only - -### Config is organised in 4 sections: - - * metadata - * onload defines the set of action to be executed at startup time - * control defines the set of controls with corresponding actions - * event define the set of actions to be executed when receiving a given signal - -### Metadata - -As today matadata is only used for documentation purpose. - * label + version mandatory - * info optional - -### OnLoad section - -Defines startup time configuration. Onload may provide multiple initialisation profiles, each with a different label. - * label is mandatory. Label is used to select onload profile at initialisation through DispatchOneOnLoad("onload-label") API; - * info is optional - * plugin provides optional unique plugin name. Plugin should follow "onload-bindername-xxxxx.ctlso" patern - and are search into CONTROL_PLUGIN_PATH. When defined controller will execute user provided function context=CTLP_ONLOAD(label,version,info). - The context returned by this routine is provided back to any C routines call later by the controller. Note that Lua2C function - are prefix in Lua script with plugin label (eg: MyPlug: in following config sample) - * lua2c list of Lua commands shipped with provided plugin. - * require list of binding that should be initialised before the controller starts. Note that some listed requirer binding might be absent, - nevertheless any present binding from this list will be started before controller binding, missing ones generate a warning. - * action the list of action to execute during loadtime. Any failure in action will prevent controller binding from starting. - -### Control section - -Defines a list of controls that are accessible through (api="control", verb="request", control="control-label"). - - * label mandatory - * info optional - * permissions Cynara needed privileges to request this control (same as AppFw-V2) - * action the list of actions - -### Event section - -Defines a list of actions to be executed on event reception. Even can do anything a controller can (change state, -send back signal, ...) eg: if a controller subscribes to vehicule speed, then speed-event may ajust master-volume to speed. - - * label mandatory - * info optional - * action the list of actions - -### Actions Categories - -Controler support tree categories of actions. Each action return a status status where 0=success and 1=failure. - * AppFw API, Provides a generic model to request other bindings. Requested binding can be local (eg: ALSA/UCM) or - external (eg: vehicle signalling). - * api provides requested binding API name - * verb provides verb to requested binding - * args optionally provides a jsonc object for targeted binding API. Note that 'args' are statically defined - in JSON configuration file. Controler client may also provided its own arguments from the query list. Targeted - binding receives both arguments defined in the config file and the argument provided by controller client. - * C-API, when defined in the onload section, the plugin may provided C native API with CTLP-CAPI(apiname, label, args, query, context). - Plugin may also create Lua command with CTLP-Lua2C(LuaFuncName, label, args, query, context). Where args+query are JSONC object - and context the value return from CTLP_ONLOAD function. Any missing value is set to NULL. - * Lua-API, when compiled with Lua option, the controller support action defined directly in Lua script. During "onload" phase the - controller search in CONTROL_Lua_PATH file with pattern "onload-bindername-xxxx.lua". Any file corresponding to this pattern - is automatically loaded. Any function defined in those Lua script can be called through a controller action. Lua functions receive - three parameters (label, args, query). - -Note: Lua added functions systematically prefix. AGL standard AppFw functions are prefixed with AGL: (eg: AGL:notice(), AGL_success(), ...). -User Lua functions added though the plugin and CTLP_Lua2C are prefix with plugin label (eg: MyPlug:HelloWorld1). - -### Debugging Facilities - -Controler Lua script are check for syntax from CMAKE template with Luac. When needed to go further an developer API allow to -execute directly Lua command within controller context from Rest/Ws (api=control, verb=lua_doscript). DoScript API takes two -other optional arguments func=xxxx where xxxx is the function to execute within Lua script and args a JSON object to provide -input parameter. When funcname is not given by default the controller try to execute middle filename doscript-xxxx-????.lua. - -When executed from controller Lua script may use any AppFw Apis as well as any L2C user defined commands in plugin. - -## Config Sample - -Here after a simple configuration sample. - -``` -{ - "$schema": "ToBeDone", - "metadata": { - "label": "sample-audio-control", - "info": "Provide Default Audio Policy for Multimedia, Navigation and Emergency", - "version": "1.0" - }, - "onload": [{ - "label": "onload-default", - "info": "onload initialisation config", - "plugin": { - "label" : "MyPlug", - "sharelib": "ctl-audio-plugin-sample.ctlso", - "lua2c": ["Lua2cHelloWorld1", "Lua2cHelloWorld2"] - }, - "require": ["intel-hda", "jabra-usb", "scarlett-usb"], - "actions": [ - { - "label": "onload-sample-cb", - "info": "Call control sharelib install entrypoint", - "callback": "SamplePolicyInit", - "args": { - "arg1": "first_arg", - "nextarg": "second arg value" - } - }, { - "label": "onload-sample-api", - "info": "Assert AlsaCore Presence", - "api": "alsacore", - "verb": "ping", - "args": "test" - }, { - "label": "onload-hal-lua", - "info": "Load avaliable HALs", - "lua": "Audio_Init_Hal" - } - ] - }], - "controls": - [ - { - "label": "multimedia", - "permissions": "urn:AGL:permission:audio:public:mutimedia", - "actions": { - "label": "multimedia-control-lua", - "info": "Call Lua Script function Test_Lua_Engin", - "lua": "Audio_Set_Multimedia" - } - }, { - "label": "navigation", - "permissions": "urn:AGL:permission:audio:public:navigation", - "actions": { - "label": "navigation-control-lua", - "info": "Call Lua Script to set Navigation", - "lua": "Audio_Set_Navigation" - } - }, { - "label": "emergency", - "permissions": "urn:AGL:permission:audio:public:emergency", - "actions": { - "label": "emergency-control-ucm", - "lua": "Audio_Set_Emergency" - } - }, { - "label": "multi-step-sample", - "info" : "all actions must succeed for control to be accepted", - "actions": [{ - "label": "multimedia-control-cb", - "info": "Call Sharelib Sample Callback", - "callback": "sampleControlNavigation", - "args": { - "arg1": "snoopy", - "arg2": "toto" - } - }, { - "label": "navigation-control-ucm", - "api": "alsacore", - "verb": "ping", - "args": { - "test": "navigation" - } - }, { - "label": "navigation-control-lua", - "info": "Call Lua Script to set Navigation", - "lua": "Audio_Set_Navigation" - }] - } - ], - "events": - [ - { - "label": "Vehicle-Speed", - "info": "Action when Vehicule speed change", - "actions": [ - { - "label": "speed-action-1", - "callback": "Blink-when-over-130", - "args": { - "speed": 130 - "blink-speed": 1000 - } - }, { - "label": "Adjust-Volume", - "lua": "Adjust_Volume_To_Speed", - } - ] - }, - { - "label": "Reverse-Engage", - "info": "When Reverse Gear is Engage", - "actions": [ - { - "label": "Display-Rear-Camera", - "callback": "Display-Rear-Camera", - }, { - "label": "Prevent-Phone-Call", - "api" : "phone", - "verb" : "status", - "args": { - "call-accepted": false - } - } - ] - }, - { - "label": "Neutral-Engage", - "info": "When Reverse Neutral is Engage", - "actions": [ - { - "label": "Authorize-Video", - "api" : "video", - "verb" : "status", - "args": { - "tv-accepted": true - } - } - ] - } - ] -} - -``` - diff --git a/Controler-afb/ctl-apidef.h b/Controler-afb/ctl-apidef.h deleted file mode 100644 index 848fd14..0000000 --- a/Controler-afb/ctl-apidef.h +++ /dev/null @@ -1,129 +0,0 @@ - -static const char _afb_description_v2_control[] = - "{\"openapi\":\"3.0.0\",\"$schema\":\"http:iot.bzh/download/openapi/schem" - "a-3.0/default-schema.json\",\"info\":{\"description\":\"\",\"title\":\"c" - "ontroler\",\"version\":\"1.0\",\"x-binding-c-generator\":{\"api\":\"cont" - "rol\",\"version\":2,\"prefix\":\"ctlapi_\",\"postfix\":\"\",\"start\":nu" - "ll,\"onevent\":\"DispatchOneEvent\",\"init\":\"CtlBindingInit\",\"scope\"" - ":\"\",\"private\":false}},\"servers\":[{\"url\":\"ws://{host}:{port}/api" - "/polctl\",\"description\":\"Unicens2 API.\",\"variables\":{\"host\":{\"d" - "efault\":\"localhost\"},\"port\":{\"default\":\"1234\"}},\"x-afb-events\"" - ":[{\"$ref\":\"#/components/schemas/afb-event\"}]}],\"components\":{\"sch" - "emas\":{\"afb-reply\":{\"$ref\":\"#/components/schemas/afb-reply-v2\"},\"" - "afb-event\":{\"$ref\":\"#/components/schemas/afb-event-v2\"},\"afb-reply" - "-v2\":{\"title\":\"Generic response.\",\"type\":\"object\",\"required\":" - "[\"jtype\",\"request\"],\"properties\":{\"jtype\":{\"type\":\"string\",\"" - "const\":\"afb-reply\"},\"request\":{\"type\":\"object\",\"required\":[\"" - "status\"],\"properties\":{\"status\":{\"type\":\"string\"},\"info\":{\"t" - "ype\":\"string\"},\"token\":{\"type\":\"string\"},\"uuid\":{\"type\":\"s" - "tring\"},\"reqid\":{\"type\":\"string\"}}},\"response\":{\"type\":\"obje" - "ct\"}}},\"afb-event-v2\":{\"type\":\"object\",\"required\":[\"jtype\",\"" - "event\"],\"properties\":{\"jtype\":{\"type\":\"string\",\"const\":\"afb-" - "event\"},\"event\":{\"type\":\"string\"},\"data\":{\"type\":\"object\"}}" - "}},\"x-permissions\":{\"monitor\":{\"permission\":\"urn:AGL:permission:a" - "udio:public:monitor\"},\"multimedia\":{\"permission\":\"urn:AGL:permissi" - "on:audio:public:monitor\"},\"navigation\":{\"permission\":\"urn:AGL:perm" - "ission:audio:public:monitor\"},\"emergency\":{\"permission\":\"urn:AGL:p" - "ermission:audio:public:emergency\"}},\"responses\":{\"200\":{\"descripti" - "on\":\"A complex object array response\",\"content\":{\"application/json" - "\":{\"schema\":{\"$ref\":\"#/components/schemas/afb-reply\"}}}}}},\"path" - "s\":{\"/monitor\":{\"description\":\"Subcribe Audio Agent Policy Control" - " End\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions" - "/monitor\"},\"parameters\":[{\"in\":\"query\",\"name\":\"event_patern\"," - "\"required\":true,\"schema\":{\"type\":\"string\"}}],\"responses\":{\"20" - "0\":{\"$ref\":\"#/components/responses/200\"}}}},\"/timer_test\":{\"desc" - "ription\":\"Pause Resume Test\",\"get\":{\"x-permissions\":{\"$ref\":\"#" - "/components/x-permissions/monitor\"},\"parameters\":[{\"in\":\"query\",\"" - "name\":\"delay\",\"required\":false,\"schema\":{\"type\":\"interger\"}}," - "{\"in\":\"query\",\"name\":\"count\",\"required\":false,\"schema\":{\"ty" - "pe\":\"interger\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/res" - "ponses/200\"}}}},\"/dispatch\":{\"description\":\"Request Access to Navi" - "gation Audio Channel.\",\"get\":{\"x-permissions\":{\"$ref\":\"#/compone" - "nts/x-permissions/navigation\"},\"parameters\":[{\"in\":\"query\",\"name" - "\":\"zone\",\"required\":false,\"schema\":{\"type\":\"string\"}}],\"resp" - "onses\":{\"200\":{\"$ref\":\"#/components/responses/200\"}}}},\"/lua_doc" - "all\":{\"description\":\"Execute LUA string script.\",\"get\":{\"x-permi" - "ssions\":{\"$ref\":\"#/components/x-permissions/navigation\"},\"paramete" - "rs\":[{\"in\":\"query\",\"name\":\"func\",\"required\":true,\"schema\":{" - "\"type\":\"string\"}},{\"in\":\"query\",\"name\":\"args\",\"required\":f" - "alse,\"schema\":{\"type\":\"array\"}}],\"responses\":{\"200\":{\"$ref\":" - "\"#/components/responses/200\"}}}},\"/lua_dostring\":{\"description\":\"" - "Execute LUA string script.\",\"get\":{\"x-permissions\":{\"$ref\":\"#/co" - "mponents/x-permissions/navigation\"},\"parameters\":[{\"in\":\"query\",\"" - "required\":true,\"schema\":{\"type\":\"string\"}}],\"responses\":{\"200\"" - ":{\"$ref\":\"#/components/responses/200\"}}}},\"/lua_doscript\":{\"descr" - "iption\":\"Execute LUA string script.\",\"get\":{\"x-permissions\":{\"$r" - "ef\":\"#/components/x-permissions/navigation\"},\"parameters\":[{\"in\":" - "\"query\",\"name\":\"filename\",\"required\":true,\"schema\":{\"type\":\"" - "string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/20" - "0\"}}}}}}" -; - -static const struct afb_auth _afb_auths_v2_control[] = { - { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:monitor" } -}; - - void ctlapi_monitor(struct afb_req req); - void ctlapi_timer_test(struct afb_req req); - void ctlapi_dispatch(struct afb_req req); - void ctlapi_lua_docall(struct afb_req req); - void ctlapi_lua_dostring(struct afb_req req); - void ctlapi_lua_doscript(struct afb_req req); - -static const struct afb_verb_v2 _afb_verbs_v2_control[] = { - { - .verb = "monitor", - .callback = ctlapi_monitor, - .auth = &_afb_auths_v2_control[0], - .info = NULL, - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "timer_test", - .callback = ctlapi_timer_test, - .auth = &_afb_auths_v2_control[0], - .info = NULL, - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "dispatch", - .callback = ctlapi_dispatch, - .auth = &_afb_auths_v2_control[0], - .info = NULL, - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "lua_docall", - .callback = ctlapi_lua_docall, - .auth = &_afb_auths_v2_control[0], - .info = NULL, - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "lua_dostring", - .callback = ctlapi_lua_dostring, - .auth = &_afb_auths_v2_control[0], - .info = NULL, - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "lua_doscript", - .callback = ctlapi_lua_doscript, - .auth = &_afb_auths_v2_control[0], - .info = NULL, - .session = AFB_SESSION_NONE_V2 - }, - { .verb = NULL } -}; - -const struct afb_binding_v2 afbBindingV2 = { - .api = "control", - .specification = _afb_description_v2_control, - .info = NULL, - .verbs = _afb_verbs_v2_control, - .preinit = NULL, - .init = CtlBindingInit, - .onevent = DispatchOneEvent, - .noconcurrency = 0 -}; - diff --git a/Controler-afb/ctl-apidef.json b/Controler-afb/ctl-apidef.json deleted file mode 100644 index 621fd5e..0000000 --- a/Controler-afb/ctl-apidef.json +++ /dev/null @@ -1,281 +0,0 @@ -{ - "openapi": "3.0.0", - "$schema": "http:iot.bzh/download/openapi/schema-3.0/default-schema.json", - "info": { - "description": "", - "title": "controler", - "version": "1.0", - "x-binding-c-generator": { - "api": "control", - "version": 2, - "prefix": "ctlapi_", - "postfix": "", - "start": null, - "onevent": "DispatchOneEvent", - "init": "CtlBindingInit", - "scope": "", - "private": false - } - }, - "servers": [ - { - "url": "ws://{host}:{port}/api/polctl", - "description": "Unicens2 API.", - "variables": { - "host": { - "default": "localhost" - }, - "port": { - "default": "1234" - } - }, - "x-afb-events": [ - { - "$ref": "#/components/schemas/afb-event" - } - ] - } - ], - "components": { - "schemas": { - "afb-reply": { - "$ref": "#/components/schemas/afb-reply-v2" - }, - "afb-event": { - "$ref": "#/components/schemas/afb-event-v2" - }, - "afb-reply-v2": { - "title": "Generic response.", - "type": "object", - "required": ["jtype", "request"], - "properties": { - "jtype": { - "type": "string", - "const": "afb-reply" - }, - "request": { - "type": "object", - "required": ["status"], - "properties": { - "status": { - "type": "string" - }, - "info": { - "type": "string" - }, - "token": { - "type": "string" - }, - "uuid": { - "type": "string" - }, - "reqid": { - "type": "string" - } - } - }, - "response": { - "type": "object" - } - } - }, - "afb-event-v2": { - "type": "object", - "required": ["jtype", "event"], - "properties": { - "jtype": { - "type": "string", - "const": "afb-event" - }, - "event": { - "type": "string" - }, - "data": { - "type": "object" - } - } - } - }, - "x-permissions": { - "monitor": { - "permission": "urn:AGL:permission:audio:public:monitor" - }, - "multimedia": { - "permission": "urn:AGL:permission:audio:public:monitor" - }, - "navigation": { - "permission": "urn:AGL:permission:audio:public:monitor" - }, - "emergency": { - "permission": "urn:AGL:permission:audio:public:emergency" - } - }, - "responses": { - "200": { - "description": "A complex object array response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/afb-reply" - } - } - } - } - } - }, - "paths": { - "/monitor": { - "description": "Subcribe Audio Agent Policy Control End", - "get": { - "x-permissions": { - "$ref": "#/components/x-permissions/monitor" - }, - "parameters": [ - { - "in": "query", - "name": "event_patern", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/200" - } - } - } - }, - "/timer_test": { - "description": "Pause Resume Test", - "get": { - "x-permissions": { - "$ref": "#/components/x-permissions/monitor" - }, - "parameters": [ - { - "in": "query", - "name": "delay", - "required": false, - "schema": { - "type": "interger" - } - }, - { - "in": "query", - "name": "count", - "required": false, - "schema": { - "type": "interger" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/200" - } - } - } - }, - "/dispatch": { - "description": "Request Access to Navigation Audio Channel.", - "get": { - "x-permissions": { - "$ref": "#/components/x-permissions/navigation" - }, - "parameters": [ - { - "in": "query", - "name": "zone", - "required": false, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/200" - } - } - } - }, - "/lua_docall": { - "description": "Execute LUA string script.", - "get": { - "x-permissions": { - "$ref": "#/components/x-permissions/navigation" - }, - "parameters": [ - { - "in": "query", - "name": "func", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "args", - "required": false, - "schema": { - "type": "array" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/200" - } - } - } - }, - "/lua_dostring": { - "description": "Execute LUA string script.", - "get": { - "x-permissions": { - "$ref": "#/components/x-permissions/navigation" - }, - "parameters": [ - { - "in": "query", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/200" - } - } - } - }, - "/lua_doscript": { - "description": "Execute LUA string script.", - "get": { - "x-permissions": { - "$ref": "#/components/x-permissions/navigation" - }, - "parameters": [ - { - "in": "query", - "name": "filename", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/200" - } - } - } - } - } -} diff --git a/Controler-afb/ctl-binding.c b/Controler-afb/ctl-binding.c deleted file mode 100644 index 8771fc0..0000000 --- a/Controler-afb/ctl-binding.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2016 "IoT.bzh" - * Author Fulup Ar Foll - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define _GNU_SOURCE -#include -#include -#include - -#include "audio-common.h" -#include "ctl-binding.h" - - - -// Include Binding Stub generated from Json OpenAPI -#include "ctl-apidef.h" - - -PUBLIC void ctlapi_monitor (afb_req request) { - - // subscribe Client to event - int err = afb_req_subscribe(request, TimerEvtGet()); - if (err != 0) { - afb_req_fail_f(request, "register-event", "Fail to subscribe binder event"); - goto OnErrorExit; - } - - afb_req_success(request, NULL, NULL); - - OnErrorExit: - return; -} - -// Create Binding Event at Init -PUBLIC int CtlBindingInit () { - - int errcount=0; - - errcount += TimerEvtInit(); - errcount += DispatchInit(); -#ifdef CONTROL_SUPPORT_LUA - errcount += LuaLibInit(); -#endif - - // now that everything is initialised execute the onload action - if (!errcount) - errcount += DispatchOnLoad(CONTROL_ONLOAD_DEFAULT); - - AFB_DEBUG ("Audio Policy Control Binding Done errcount=%d", errcount); - return errcount; -} - diff --git a/Controler-afb/ctl-binding.h b/Controler-afb/ctl-binding.h deleted file mode 100644 index c998007..0000000 --- a/Controler-afb/ctl-binding.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2016 "IoT.bzh" - * Author Fulup Ar Foll - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CONTROLER_BINDING_INCLUDE -#define CONTROLER_BINDING_INCLUDE - -#define AFB_BINDING_VERSION 2 -#include -#include -#include -#include -#include - - -#ifdef CONTROL_SUPPORT_LUA -#include "lua.h" -#include "lauxlib.h" -#include "lualib.h" -#endif - -#ifndef PUBLIC - #define PUBLIC -#endif -#define STATIC static - -#ifndef UNUSED_ARG -#define UNUSED_ARG(x) UNUSED_ ## x __attribute__((__unused__)) -#define UNUSED_FUNCTION(x) __attribute__((__unused__)) UNUSED_ ## x -#endif - -// polctl-binding.c -PUBLIC int CtlBindingInit (); - -// ctl-timerevt.c -// ---------------------- -typedef int (*timerCallbackT)(void *context); - -typedef struct TimerHandleS { - int count; - int delay; - const char*label; - void *context; - timerCallbackT callback; - sd_event_source *evtSource; -} TimerHandleT; - -PUBLIC int TimerEvtInit (void); -PUBLIC afb_event TimerEvtGet(void); -PUBLIC void ctlapi_event_test (afb_req request); -PUBLIC void TimerEvtStart(TimerHandleT *timerHandle, timerCallbackT callback, void *context); -PUBLIC void TimerEvtStop(TimerHandleT *timerHandle); - -// ctl-policy -// ----------- - -typedef enum { - CTL_MODE_NONE=0, - CTL_MODE_API, - CTL_MODE_CB, - CTL_MODE_LUA, -} CtlRequestModeT; - -typedef struct DispatchActionS{ - const char *info; - const char* label; - CtlRequestModeT mode; - const char* api; - const char* call; - json_object *argsJ; - int timeout; - int (*actionCB)(const char*label, json_object *argsJ, json_object *queryJ, void *context); -} DispatchActionT; -typedef int (*Lua2cFunctionT)(char *funcname, json_object *argsJ, void*context); - -PUBLIC int DispatchInit(void); -PUBLIC int DispatchOnLoad(const char *onLoadLabel); -PUBLIC void DispatchOneEvent(const char *evtLabel, json_object *eventJ); -PUBLIC int DispatchOneL2c(lua_State* luaState, char *funcname, Lua2cFunctionT callback); -PUBLIC void ctlapi_dispatch (afb_req request); - -#ifdef CONTROL_SUPPORT_LUA -// ctl-lua.c -typedef int (*Lua2cWrapperT) (lua_State* luaState, char *funcname, Lua2cFunctionT callback); - -#define CTLP_LUA2C(FuncName,label,argsJ, context) static int FuncName(char*label,json_object*argsJ, void*context);\ - int lua2c_ ## FuncName(lua_State* luaState){return((*Lua2cWrap)(luaState, MACRO_STR_VALUE(FuncName), FuncName));};\ - static int FuncName(char* label, json_object* argsJ, void* context) - -typedef enum { - LUA_DOCALL, - LUA_DOSTRING, - LUA_DOSCRIPT, -} LuaDoActionT; - -PUBLIC int LuaLibInit (); -PUBLIC void LuaL2cNewLib(const char *label, luaL_Reg *l2cFunc, int count); -PUBLIC int Lua2cWrapper(lua_State* luaState, char *funcname, Lua2cFunctionT callback, void *context); -PUBLIC int LuaCallFunc (DispatchActionT *action, json_object *queryJ); -PUBLIC void ctlapi_lua_docall (afb_req request); -PUBLIC void ctlapi_lua_dostring (afb_req request); -PUBLIC void ctlapi_lua_doscript (afb_req request); - -#else - typedef void* Lua2cWrapperT; -#endif // CONTROL_SUPPORT_LUA - - -// sharelib ctl-plugin* -typedef struct { - long magic; - char *label; -} CtlPluginMagicT; - - -#define MACRO_STR_VALUE(arg) #arg -#define CTLP_REGISTER(pluglabel) CtlPluginMagicT CtlPluginMagic={.magic=CTL_PLUGIN_MAGIC,.label=pluglabel}; struct afb_binding_data_v2; Lua2cWrapperT Lua2cWrap; -#define CTLP_ONLOAD(label,version,info) void* CtlPluginOnload(char* label, char* version, char* info) -#define CTLP_CAPI(funcname,label,argsJ, queryJ, context) int funcname(char* label, json_object* argsJ, json_object* queryJ, void* context) - - - -#endif // CONTROLER_BINDING_INCLUDE diff --git a/Controler-afb/ctl-dispatch.c b/Controler-afb/ctl-dispatch.c deleted file mode 100644 index 1afb846..0000000 --- a/Controler-afb/ctl-dispatch.c +++ /dev/null @@ -1,643 +0,0 @@ -/* - * Copyright (C) 2016 "IoT.bzh" - * Author Fulup Ar Foll - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, something express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Reference: - * Json load using json_unpack https://jansson.readthedocs.io/en/2.9/apiref.html#parsing-and-validating-values - */ - -#define _GNU_SOURCE -#include -#include -#include - -#include "ctl-binding.h" - -typedef void*(*DispatchPluginInstallCbT)(const char* label, const char*version, const char*info); - -static afb_req NULL_AFBREQ = {}; - -typedef enum { - CTL_DISPATCH_ONLOAD, - CTL_DISPATCH_CONTROL, - CTL_DISPATCH_EVENT, -} DispatchClassT; - -typedef struct { - const char* label; - const char *info; - DispatchActionT *actions; -} DispatchHandleT; - -typedef struct { - const char *label; - const char *info; - void *context; - char *sharelib; - void *dlHandle; - luaL_Reg *l2cFunc; - int l2cCount; -} DispatchPluginT; - -typedef struct { - const char* label; - const char *info; - const char *version; - DispatchPluginT *plugin; - DispatchHandleT **onloads; - DispatchHandleT **events; - DispatchHandleT **controls; -} DispatchConfigT; - -// global config handle -STATIC DispatchConfigT *configHandle = NULL; - -STATIC int DispatchControlToIndex(DispatchHandleT **controls, const char* controlLabel) { - - for (int idx = 0; controls[idx]; idx++) { - if (!strcasecmp(controlLabel, controls[idx]->label)) return idx; - } - return -1; -} - -STATIC int DispatchOneControl(DispatchHandleT **controls, const char* controlLabel, json_object *queryJ, afb_req request) { - int err; - - if (!configHandle) { - AFB_ERROR("DISPATCH-CTL-API: (Hoops/Bug!!!) No Config Loaded"); - goto OnErrorExit; - } - - if (!configHandle->controls) { - AFB_ERROR("DISPATCH-CTL-API: No Control Action in Json config label=%s version=%s", configHandle->label, configHandle->version); - goto OnErrorExit; - } - - int index = DispatchControlToIndex(controls, controlLabel); - if (index < 0 || !controls[index]->actions) { - AFB_ERROR("DISPATCH-CTL-API:NotFound/Error label=%s in Json Control Config File", controlLabel); - goto OnErrorExit; - } - - // Fulup (Bug/Feature) in current version is unique to every onload profile - if (configHandle->plugin && configHandle->plugin->l2cCount) { - LuaL2cNewLib (configHandle->plugin->label, configHandle->plugin->l2cFunc, configHandle->plugin->l2cCount); - } - - // loop on action for this control - DispatchActionT *actions = controls[index]->actions; - for (int idx = 0; actions[idx].label; idx++) { - - switch (actions[idx].mode) { - case CTL_MODE_API: - { - json_object *returnJ; - - json_object_get(actions[idx].argsJ); // make sure afb_service_call does not free the argsJ - int err = afb_service_call_sync(actions[idx].api, actions[idx].call, actions[idx].argsJ, &returnJ); - if (err) { - static const char*format = "DispatchOneControl(Api) api=%s verb=%s args=%s"; - if (afb_req_is_valid(request))afb_req_fail_f(request, "DISPATCH-CTL-MODE:API", format, actions[idx].label, actions[idx].api, actions[idx].call); - else AFB_ERROR(format, actions[idx].api, actions[idx].call, actions[idx].label); - goto OnErrorExit; - } - break; - } - -#ifdef CONTROL_SUPPORT_LUA - case CTL_MODE_LUA: - err = LuaCallFunc(&actions[idx], queryJ); - if (err) { - static const char*format = "DispatchOneControl(Lua) label=%s func=%s args=%s"; - if (afb_req_is_valid(request)) afb_req_fail_f(request, "DISPATCH-CTL-MODE:Lua", format, actions[idx].label, actions[idx].call, json_object_get_string(actions[idx].argsJ)); - else AFB_ERROR(format, actions[idx].label, actions[idx].call, json_object_get_string(actions[idx].argsJ)); - goto OnErrorExit; - } - break; -#endif - - case CTL_MODE_CB: - err = (*actions[idx].actionCB) (actions[idx].label, actions[idx].argsJ, queryJ, configHandle->plugin->context); - if (err) { - static const char*format = "DispatchOneControl(Callback) label%s func=%s args=%s"; - if (afb_req_is_valid(request)) afb_req_fail_f(request, "DISPATCH-CTL-MODE:Cb", format, actions[idx].label, actions[idx].call, json_object_get_string(actions[idx].argsJ)); - else AFB_ERROR(format, actions[idx].label, actions[idx].call, json_object_get_string(actions[idx].argsJ)); - goto OnErrorExit; - } - break; - - default: - { - static const char*format = "DispatchOneControl(unknown) mode control=%s action=%s"; - AFB_ERROR(format, controls[index]->label); - if (afb_req_is_valid(request))afb_req_fail_f(request, "DISPATCH-CTL-MODE:Unknown", format, controls[index]->label); - } - } - } - - // everything when fine - if (afb_req_is_valid(request))afb_req_success(request, NULL, controls[index]->label); - return 0; - -OnErrorExit: - return -1; -} - - -// Event name is mapped on control label and executed as a standard control - -PUBLIC void DispatchOneEvent(const char *evtLabel, json_object *eventJ) { - DispatchHandleT **events = configHandle->events; - - (void) DispatchOneControl(events, evtLabel, eventJ, NULL_AFBREQ); -} - -// Event name is mapped on control label and executed as a standard control - -PUBLIC int DispatchOnLoad(const char *onLoadLabel) { - DispatchHandleT **onloads = configHandle->onloads; - - int err = DispatchOneControl(onloads, onLoadLabel, NULL, NULL_AFBREQ); - return err; -} - -PUBLIC void ctlapi_dispatch(afb_req request) { - DispatchHandleT **controls = configHandle->controls; - json_object *queryJ, *argsJ; - const char *target; - - queryJ = afb_req_json(request); - int err = wrap_json_unpack(queryJ, "{s:s, s:o !}", "target", &target, "args", &argsJ); - if (err) { - afb_req_fail_f(request, "CTL-DISPTACH-INVALID", "missing target or args not a valid json object query=%s", json_object_get_string(queryJ)); - goto OnErrorExit; - } - - (void) DispatchOneControl(controls, target, argsJ, request); - -OnErrorExit: - return; -} - -// Wrapper to Lua2c plugin command add context dans delegate to LuaWrapper -PUBLIC int DispatchOneL2c(lua_State* luaState, char *funcname, Lua2cFunctionT callback) { -#ifndef CONTROL_SUPPORT_LUA - AFB_ERROR("DISPATCH-ONE-L2C: LUA support not selected (cf:CONTROL_SUPPORT_LUA) in config.cmake"); - return 1; -#else - int err=Lua2cWrapper(luaState, funcname, callback, configHandle->plugin->context); - return err; -#endif -} - - -// List Avaliable Configuration Files - -PUBLIC void ctlapi_config(struct afb_req request) { - json_object*tmpJ; - char *dirList; - - json_object* queryJ = afb_req_json(request); - if (queryJ && json_object_object_get_ex(queryJ, "cfgpath", &tmpJ)) { - dirList = strdup(json_object_get_string(tmpJ)); - } else { - dirList = strdup(CONTROL_CONFIG_PATH); - AFB_NOTICE("CONFIG-MISSING: use default CONTROL_CONFIG_PATH=%s", CONTROL_CONFIG_PATH); - } - - // get list of config file - struct json_object *responseJ = ScanForConfig(dirList, CTL_SCAN_RECURSIVE, "onload", "json"); - - if (json_object_array_length(responseJ) == 0) { - afb_req_fail(request, "CONFIGPATH:EMPTY", "No Config Found in CONTROL_CONFIG_PATH"); - } else { - afb_req_success(request, responseJ, NULL); - } - - return; -} - -// unpack individual action object - -STATIC int DispatchLoadOneAction(DispatchConfigT *controlConfig, json_object *actionJ, DispatchActionT *action) { - char *api = NULL, *verb = NULL, *callback = NULL, *lua = NULL; - int err, modeCount = 0; - - err = wrap_json_unpack(actionJ, "{ss,s?s,s?s,s?s,s?s,s?s,s?o !}" - , "label", &action->label, "info", &action->info, "callback", &callback, "lua", &lua, "api", &api, "verb", &verb, "args", &action->argsJ); - if (err) { - AFB_ERROR("DISPATCH-LOAD-ACTION Missing something label|info|callback|lua|(api+verb)|args in %s", json_object_get_string(actionJ)); - goto OnErrorExit; - } - - if (lua) { - action->mode = CTL_MODE_LUA; - action->call = lua; - modeCount++; - } - - if (api && verb) { - action->mode = CTL_MODE_API; - action->api = api; - action->call = verb; - modeCount++; - } - - if (callback && controlConfig->plugin) { - action->mode = CTL_MODE_CB; - action->call = callback; - modeCount++; - - action->actionCB = dlsym(controlConfig->plugin->dlHandle, callback); - if (!action->actionCB) { - AFB_ERROR("DISPATCH-LOAD-ACTION fail to find calbback=%s in %s", callback, controlConfig->plugin->sharelib); - goto OnErrorExit; - } - } - - // make sure at least one mode is selected - if (modeCount == 0) { - AFB_ERROR("DISPATCH-LOAD-ACTION No Action Selected lua|callback|(api+verb) in %s", json_object_get_string(actionJ)); - goto OnErrorExit; - } - - if (modeCount > 1) { - AFB_ERROR("DISPATCH-LOAD-ACTION:ToMany arguments lua|callback|(api+verb) in %s", json_object_get_string(actionJ)); - goto OnErrorExit; - } - return 0; - -OnErrorExit: - return -1; -}; - -STATIC DispatchActionT *DispatchLoadActions(DispatchConfigT *controlConfig, json_object *actionsJ) { - int err; - DispatchActionT *actions; - - // action array is close with a nullvalue; - if (json_object_get_type(actionsJ) == json_type_array) { - int count = json_object_array_length(actionsJ); - actions = calloc(count + 1, sizeof (DispatchActionT)); - - for (int idx = 0; idx < count; idx++) { - json_object *actionJ = json_object_array_get_idx(actionsJ, idx); - err = DispatchLoadOneAction(controlConfig, actionJ, &actions[idx]); - if (err) goto OnErrorExit; - } - - } else { - actions = calloc(2, sizeof (DispatchActionT)); - err = DispatchLoadOneAction(controlConfig, actionsJ, &actions[0]); - if (err) goto OnErrorExit; - } - - return actions; - -OnErrorExit: - return NULL; - -} - -STATIC DispatchHandleT *DispatchLoadControl(DispatchConfigT *controlConfig, json_object *controlJ) { - json_object *actionsJ, *permissionsJ; - int err; - - DispatchHandleT *dispatchHandle = calloc(1, sizeof (DispatchHandleT)); - err = wrap_json_unpack(controlJ, "{ss,s?s,s?o, so !}", "label", &dispatchHandle->label, "info", &dispatchHandle->info - , "permissions", &permissionsJ, "actions", &actionsJ); - if (err) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:CONTROL Missing something label|[info]|actions in %s", json_object_get_string(controlJ)); - goto OnErrorExit; - } - - dispatchHandle->actions = DispatchLoadActions(controlConfig, actionsJ); - if (!dispatchHandle->actions) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:CONTROL Error when parsing actions %s", dispatchHandle->label); - goto OnErrorExit; - } - return dispatchHandle; - -OnErrorExit: - return NULL; -} - -STATIC DispatchHandleT *DispatchLoadOnload(DispatchConfigT *controlConfig, json_object *onloadJ) { - json_object *actionsJ = NULL, *requireJ = NULL, *pluginJ = NULL; - int err; - - DispatchHandleT *dispatchHandle = calloc(1, sizeof (DispatchHandleT)); - err = wrap_json_unpack(onloadJ, "{ss,s?s,s?o,s?o,s?o !}", - "label", &dispatchHandle->label, "info", &dispatchHandle->info, "plugin", &pluginJ, "require", &requireJ, "actions", &actionsJ); - if (err) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:ONLOAD Missing something label|[info]|[plugin]|[actions] in %s", json_object_get_string(onloadJ)); - goto OnErrorExit; - } - - // best effort to initialise everything before starting - if (requireJ) { - - void DispatchRequireOneApi(json_object * bindindJ) { - const char* requireBinding = json_object_get_string(bindindJ); - err = afb_daemon_require_api(requireBinding, 1); - if (err) { - AFB_WARNING("DISPATCH-LOAD-CONFIG:REQUIRE Fail to get=%s", requireBinding); - } - } - - if (json_object_get_type(requireJ) == json_type_array) { - for (int idx = 0; idx < json_object_array_length(requireJ); idx++) { - DispatchRequireOneApi(json_object_array_get_idx(requireJ, idx)); - } - } else { - DispatchRequireOneApi(requireJ); - } - } - - if (pluginJ) { - json_object *lua2csJ = NULL; - DispatchPluginT *dPlugin= calloc(1, sizeof(DispatchPluginT)); - controlConfig->plugin = dPlugin; - - err = wrap_json_unpack(pluginJ, "{ss,s?s,ss,s?o!}", - "label", &dPlugin->label, "info", &dPlugin->info, "sharelib", &dPlugin->sharelib, "lua2c", &lua2csJ); - if (err) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:ONLOAD Plugin missing label|[info]|sharelib|[lua2c] in %s", json_object_get_string(onloadJ)); - goto OnErrorExit; - } - - // search for default policy config file - json_object *pluginPathJ = ScanForConfig(CONTROL_PLUGIN_PATH, CTL_SCAN_RECURSIVE, dPlugin->sharelib, NULL); - if (!pluginPathJ || json_object_array_length(pluginPathJ) == 0) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:PLUGIN Missing plugin=%s in path=%s", dPlugin->sharelib, CONTROL_PLUGIN_PATH); - goto OnErrorExit; - } - - char *filename; - char*fullpath; - err = wrap_json_unpack(json_object_array_get_idx(pluginPathJ, 0), "{s:s, s:s !}", "fullpath", &fullpath, "filename", &filename); - if (err) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:PLUGIN HOOPs invalid plugin file path = %s", json_object_get_string(pluginPathJ)); - goto OnErrorExit; - } - - if (json_object_array_length(pluginPathJ) > 1) { - AFB_WARNING("DISPATCH-LOAD-CONFIG:PLUGIN plugin multiple instances in searchpath will use %s/%s", fullpath, filename); - } - - char pluginpath[CONTROL_MAXPATH_LEN]; - strncpy(pluginpath, fullpath, sizeof (pluginpath)); - strncat(pluginpath, "/", sizeof (pluginpath)); - strncat(pluginpath, filename, sizeof (pluginpath)); - dPlugin->dlHandle = dlopen(pluginpath, RTLD_NOW); - if (!dPlugin->dlHandle) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:PLUGIN Fail to load pluginpath=%s err= %s", pluginpath, dlerror()); - goto OnErrorExit; - } - - CtlPluginMagicT *ctlPluginMagic = (CtlPluginMagicT*) dlsym(dPlugin->dlHandle, "CtlPluginMagic"); - if (!ctlPluginMagic || ctlPluginMagic->magic != CTL_PLUGIN_MAGIC) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:Plugin symbol'CtlPluginMagic' missing or != CTL_PLUGIN_MAGIC plugin=%s", pluginpath); - goto OnErrorExit; - } else { - AFB_NOTICE("DISPATCH-LOAD-CONFIG:Plugin %s successfully registered", ctlPluginMagic->label); - } - - // Jose hack to make verbosity visible from sharelib - struct afb_binding_data_v2 *afbHidenData = dlsym(dPlugin->dlHandle, "afbBindingV2data"); - if (afbHidenData) *afbHidenData = afbBindingV2data; - - // Push lua2cWrapper @ into plugin - Lua2cWrapperT *lua2cInPlug = dlsym(dPlugin->dlHandle, "Lua2cWrap"); -#ifndef CONTROL_SUPPORT_LUA - if (lua2cInPlug) *lua2cInPlug = NULL; -#else - // Lua2cWrapper is part of binder and not expose to dynamic link - if (lua2cInPlug) *lua2cInPlug = DispatchOneL2c; - - { - int Lua2cAddOne(luaL_Reg *l2cFunc, const char* l2cName, int index) { - char funcName[CONTROL_MAXPATH_LEN]; - strncpy(funcName, "lua2c_", sizeof(funcName)); - strncat(funcName, l2cName, sizeof(funcName)); - - Lua2cFunctionT l2cFunction= (Lua2cFunctionT)dlsym(dPlugin->dlHandle, funcName); - if (!l2cFunction) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:Plugin symbol'%s' missing err=%s", funcName, dlerror()); - return 1; - } - l2cFunc[index].func=(void*)l2cFunction; - l2cFunc[index].name=strdup(l2cName); - - return 0; - } - - int errCount = 0; - luaL_Reg *l2cFunc=NULL; - int count=0; - - // look on l2c command and push them to LUA - if (json_object_get_type(lua2csJ) == json_type_array) { - int length = json_object_array_length(lua2csJ); - l2cFunc = calloc(length + 1, sizeof (luaL_Reg)); - for (count=0; count < length; count++) { - int err; - const char *l2cName = json_object_get_string(json_object_array_get_idx(lua2csJ, count)); - err = Lua2cAddOne(l2cFunc, l2cName, count); - if (err) errCount++; - } - } else { - l2cFunc = calloc(2, sizeof (luaL_Reg)); - const char *l2cName = json_object_get_string(lua2csJ); - errCount = Lua2cAddOne(l2cFunc, l2cName, 0); - count=1; - } - if (errCount) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:Plugin %d symbols not found in plugin='%s'", errCount, pluginpath); - goto OnErrorExit; - } else { - dPlugin->l2cFunc= l2cFunc; - dPlugin->l2cCount= count; - } - } -#endif - DispatchPluginInstallCbT ctlPluginOnload = dlsym(dPlugin->dlHandle, "CtlPluginOnload"); - if (ctlPluginOnload) { - dPlugin->context = (*ctlPluginOnload) (controlConfig->label, controlConfig->version, controlConfig->info); - } - } - - dispatchHandle->actions = DispatchLoadActions(controlConfig, actionsJ); - if (!dispatchHandle->actions) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:ONLOAD Error when parsing actions %s", dispatchHandle->label); - goto OnErrorExit; - } - return dispatchHandle; - -OnErrorExit: - return NULL; -} - -STATIC DispatchConfigT *DispatchLoadConfig(const char* filepath) { - json_object *controlConfigJ, *ignoreJ; - int err; - - // Load JSON file - controlConfigJ = json_object_from_file(filepath); - if (!controlConfigJ) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:JsonLoad invalid JSON %s ", filepath); - goto OnErrorExit; - } - - AFB_INFO("DISPATCH-LOAD-CONFIG: loading config filepath=%s", filepath); - - json_object *metadataJ = NULL, *onloadsJ = NULL, *controlsJ = NULL, *eventsJ = NULL; - err = wrap_json_unpack(controlConfigJ, "{s?o,so,s?o,s?o,s?o !}", "$schema", &ignoreJ, "metadata", &metadataJ, "onload", &onloadsJ, "controls", &controlsJ, "events", &eventsJ); - if (err) { - AFB_ERROR("DISPATCH-LOAD-CONFIG Missing something metadata|[onload]|[controls]|[events] in %s", json_object_get_string(controlConfigJ)); - goto OnErrorExit; - } - - DispatchConfigT *controlConfig = calloc(1, sizeof (DispatchConfigT)); - if (metadataJ) { - err = wrap_json_unpack(metadataJ, "{ss,s?s,ss !}", "label", &controlConfig->label, "info", &controlConfig->info, "version", &controlConfig->version); - if (err) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:METADATA Missing something label|version|[label] in %s", json_object_get_string(metadataJ)); - goto OnErrorExit; - } - } - - if (onloadsJ) { - DispatchHandleT *dispatchHandle; - - if (json_object_get_type(onloadsJ) != json_type_array) { - controlConfig->onloads = (DispatchHandleT**) calloc(2, sizeof (void*)); - dispatchHandle = DispatchLoadOnload(controlConfig, onloadsJ); - controlConfig->onloads[0] = dispatchHandle; - } else { - int length = json_object_array_length(onloadsJ); - controlConfig->onloads = (DispatchHandleT**) calloc(length + 1, sizeof (void*)); - - for (int jdx = 0; jdx < length; jdx++) { - json_object *onloadJ = json_object_array_get_idx(onloadsJ, jdx); - dispatchHandle = DispatchLoadOnload(controlConfig, onloadJ); - controlConfig->onloads[jdx] = dispatchHandle; - } - } - } - - if (controlsJ) { - DispatchHandleT *dispatchHandle; - - if (json_object_get_type(controlsJ) != json_type_array) { - controlConfig->controls = (DispatchHandleT**) calloc(2, sizeof (void*)); - dispatchHandle = DispatchLoadControl(controlConfig, controlsJ); - controlConfig->controls[0] = dispatchHandle; - } else { - int length = json_object_array_length(controlsJ); - controlConfig->controls = (DispatchHandleT**) calloc(length + 1, sizeof (void*)); - - for (int jdx = 0; jdx < length; jdx++) { - json_object *controlJ = json_object_array_get_idx(controlsJ, jdx); - dispatchHandle = DispatchLoadControl(controlConfig, controlJ); - controlConfig->controls[jdx] = dispatchHandle; - } - } - } - - if (eventsJ) { - DispatchHandleT *dispatchHandle; - - if (json_object_get_type(eventsJ) != json_type_array) { - controlConfig->events = (DispatchHandleT**) calloc(2, sizeof (void*)); - dispatchHandle = DispatchLoadControl(controlConfig, eventsJ); - controlConfig->events[0] = dispatchHandle; - } else { - int length = json_object_array_length(eventsJ); - controlConfig->events = (DispatchHandleT**) calloc(length + 1, sizeof (void*)); - - for (int jdx = 0; jdx < length; jdx++) { - json_object *eventJ = json_object_array_get_idx(eventsJ, jdx); - dispatchHandle = DispatchLoadControl(controlConfig, eventJ); - controlConfig->events[jdx] = dispatchHandle; - } - } - } - - return controlConfig; - -OnErrorExit: - return NULL; -} - - -// Load default config file at init - -PUBLIC int DispatchInit() { - int index, err, luaLoaded = 0; - char controlFile [CONTROL_MAXPATH_LEN]; - - strncpy(controlFile, CONTROL_CONFIG_PRE "-", CONTROL_MAXPATH_LEN); - strncat(controlFile, GetBinderName(), CONTROL_MAXPATH_LEN); - - // search for default dispatch config file - json_object* responseJ = ScanForConfig(CONTROL_CONFIG_PATH, CTL_SCAN_RECURSIVE, controlFile, "json"); - - // We load 1st file others are just warnings - for (index = 0; index < json_object_array_length(responseJ); index++) { - json_object *entryJ = json_object_array_get_idx(responseJ, index); - - char *filename; - char*fullpath; - err = wrap_json_unpack(entryJ, "{s:s, s:s !}", "fullpath", &fullpath, "filename", &filename); - if (err) { - AFB_ERROR("DISPATCH-INIT HOOPs invalid JSON entry= %s", json_object_get_string(entryJ)); - goto OnErrorExit; - } - - if (index == 0) { - if (strcasestr(filename, controlFile)) { - char filepath[CONTROL_MAXPATH_LEN]; - strncpy(filepath, fullpath, sizeof (filepath)); - strncat(filepath, "/", sizeof (filepath)); - strncat(filepath, filename, sizeof (filepath)); - configHandle = DispatchLoadConfig(filepath); - if (!configHandle) { - AFB_ERROR("DISPATCH-INIT:ERROR Fail loading [%s]", filepath); - goto OnErrorExit; - } - luaLoaded = 1; - break; - } - } else { - AFB_WARNING("DISPATCH-INIT:WARNING Secondary Control Config Ignored %s/%s", fullpath, filename); - } - } - - // no dispatch config found remove control API from binder - if (!luaLoaded) { - AFB_WARNING("DISPATCH-INIT:WARNING Not Found Control dispatch file [%s]", controlFile); - } - - AFB_NOTICE("DISPATCH-INIT:SUCCES: Audio Control Dispatch Init"); - return 0; - -OnErrorExit: - AFB_NOTICE("DISPATCH-INIT:ERROR: Audio Control Dispatch Init"); - return 1; -} - - - diff --git a/Controler-afb/ctl-lua.c b/Controler-afb/ctl-lua.c deleted file mode 100644 index 9d2cceb..0000000 --- a/Controler-afb/ctl-lua.c +++ /dev/null @@ -1,1026 +0,0 @@ -/* - * Copyright (C) 2016 "IoT.bzh" - * Author Fulup Ar Foll - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ref: - * http://www.troubleshooters.com/codecorn/lua/lua_c_calls_lua.htm#_Anatomy_of_a_Lua_Call - * http://acamara.es/blog/2012/08/passing-variables-from-lua-5-2-to-c-and-vice-versa/ - * https://john.nachtimwald.com/2014/07/12/wrapping-a-c-library-in-lua/ - * https://gist.github.com/SONIC3D/10388137 - */ - -#define _GNU_SOURCE -#include -#include - - -#include "ctl-binding.h" -#include "wrap-json.h" - -#define LUA_FIST_ARG 2 // when using luaL_newlib calllback receive libtable as 1st arg -#define LUA_MSG_MAX_LENGTH 512 -#define JSON_ERROR (json_object*)-1 -static afb_req NULL_AFBREQ = {}; - - -static lua_State* luaState; - -#define CTX_MAGIC 123456789 -#define CTX_TOKEN "AFB_ctx" - -typedef struct { - char *name; - int count; - afb_event event; -} LuaAfbEvent; -static LuaAfbEvent *luaDefaultEvt; - -typedef struct { - int ctxMagic; - afb_req request; - void *handle; - char *info; -} LuaAfbContextT; - -typedef struct { - const char *callback; - json_object *context; - void *handle; -} LuaCallServiceT; - -typedef enum { - AFB_MSG_INFO, - AFB_MSG_WARNING, - AFB_MSG_NOTICE, - AFB_MSG_DEBUG, - AFB_MSG_ERROR, -} LuaAfbMessageT; - -/* - * Note(Fulup): I fail to use luaL_setmetatable and replaced it with a simple opaque - * handle while waiting for someone smarter than me to find a better solution - * https://stackoverflow.com/questions/45596493/lua-using-lua-newuserdata-from-lua-pcall - */ - -STATIC LuaAfbContextT *LuaCtxCheck (lua_State *luaState, int index) { - LuaAfbContextT *afbContext; - //luaL_checktype(luaState, index, LUA_TUSERDATA); - //afbContext = (LuaAfbContextT *)luaL_checkudata(luaState, index, CTX_TOKEN); - luaL_checktype(luaState, index, LUA_TLIGHTUSERDATA); - afbContext = (LuaAfbContextT *) lua_touserdata(luaState, index); - if (afbContext == NULL && afbContext->ctxMagic != CTX_MAGIC) { - luaL_error(luaState, "Fail to retrieve user data context=%s", CTX_TOKEN); - AFB_ERROR ("afbContextCheck error retrieving afbContext"); - return NULL; - } - return afbContext; -} - -STATIC LuaAfbContextT *LuaCtxPush (lua_State *luaState, afb_req request, void *handle, const char* info) { - // LuaAfbContextT *afbContext = (LuaAfbContextT *)lua_newuserdata(luaState, sizeof(LuaAfbContextT)); - // luaL_setmetatable(luaState, CTX_TOKEN); - LuaAfbContextT *afbContext = (LuaAfbContextT *)calloc(1, sizeof(LuaAfbContextT)); - lua_pushlightuserdata(luaState, afbContext); - if (!afbContext) { - AFB_ERROR ("LuaCtxPush fail to allocate user data context"); - return NULL; - } - afbContext->ctxMagic=CTX_MAGIC; - afbContext->info=strdup(info); - afbContext->request= request; - afbContext->handle= handle; - return afbContext; -} - -STATIC void LuaCtxFree (LuaAfbContextT *afbContext) { - free(afbContext->info); - free(afbContext); -} - -// Push a json structure on the stack as a LUA table -STATIC int LuaPushArgument (json_object *argsJ) { - - //AFB_NOTICE("LuaPushArgument argsJ=%s", json_object_get_string(argsJ)); - - json_type jtype= json_object_get_type(argsJ); - switch (jtype) { - case json_type_object: { - lua_newtable (luaState); - json_object_object_foreach (argsJ, key, val) { - int done = LuaPushArgument (val); - if (done) { - lua_setfield(luaState,-2, key); - } - } - break; - } - case json_type_array: { - int length= json_object_array_length(argsJ); - lua_newtable (luaState); - for (int idx=0; idx < length; idx ++) { - json_object *val=json_object_array_get_idx(argsJ, idx); - LuaPushArgument (val); - lua_seti (luaState,-2, idx); - } - break; - } - case json_type_int: - lua_pushinteger(luaState, json_object_get_int(argsJ)); - break; - case json_type_string: - lua_pushstring(luaState, json_object_get_string(argsJ)); - break; - case json_type_boolean: - lua_pushboolean(luaState, json_object_get_boolean(argsJ)); - break; - case json_type_double: - lua_pushnumber(luaState, json_object_get_double(argsJ)); - break; - case json_type_null: - AFB_WARNING("LuaPushArgument: NULL object type %s", json_object_get_string(argsJ)); - return 0; - break; - - default: - AFB_ERROR("LuaPushArgument: unsupported Json object type %s", json_object_get_string(argsJ)); - return 0; - } - return 1; -} - -STATIC json_object *LuaPopOneArg (lua_State* luaState, int idx); - -STATIC json_object *LuaTableToJson (lua_State* luaState, int index) { - int idx; - #define LUA_KEY_INDEX -2 - #define LUA_VALUE_INDEX -1 - - json_object *tableJ= json_object_new_object(); - const char *key; - char number[3]; - lua_pushnil(luaState); // 1st key - if (index < 0) index--; // nested table https://stackoverflow.com/questions/45699144/lua-nested-table-from-lua-to-c - for (idx=1; lua_next(luaState, index) != 0; idx++) { - - // uses 'key' (at index -2) and 'value' (at index -1) - if (lua_type(luaState,LUA_KEY_INDEX) == LUA_TSTRING) key= lua_tostring(luaState, LUA_KEY_INDEX); - else { - snprintf(number, sizeof(number),"%d", idx); - key=number; - } - json_object *argJ= LuaPopOneArg(luaState, LUA_VALUE_INDEX); - json_object_object_add(tableJ, key, argJ); - lua_pop(luaState, 1); // removes 'value'; keeps 'key' for next iteration - } - - // Query is empty free empty json object - if (idx == 1) { - json_object_put(tableJ); - return NULL; - } - return tableJ; -} - -STATIC json_object *LuaPopOneArg (lua_State* luaState, int idx) { - json_object *value=NULL; - - int luaType = lua_type(luaState, idx); - switch(luaType) { - case LUA_TNUMBER: { - lua_Number number= lua_tonumber(luaState, idx);; - int nombre = (int)number; // evil trick to determine wether n fits in an integer. (stolen from ltcl.c) - if (number == nombre) { - value= json_object_new_int((int)number); - } else { - value= json_object_new_double(number); - } - break; - } - case LUA_TBOOLEAN: - value= json_object_new_boolean(lua_toboolean(luaState, idx)); - break; - case LUA_TSTRING: - value= json_object_new_string(lua_tostring(luaState, idx)); - break; - case LUA_TTABLE: - value= LuaTableToJson(luaState, idx); - break; - case LUA_TNIL: - value=json_object_new_string("nil") ; - break; - case LUA_TUSERDATA: - value=json_object_new_int64((int64_t)lua_touserdata(luaState, idx)); // store userdata as int !!! - break; - - default: - AFB_NOTICE ("LuaPopOneArg: script returned Unknown/Unsupported idx=%d type:%d/%s", idx, luaType, lua_typename(luaState, luaType)); - value=NULL; - } - - return value; -} - -static json_object *LuaPopArgs (lua_State* luaState, int start) { - json_object *responseJ; - - int stop = lua_gettop(luaState); - if(stop-start <0) return NULL; - - // start at 2 because we are using a function array lib - if (start == stop) { - responseJ=LuaPopOneArg (luaState, start); - } else { - // loop on remaining return arguments - responseJ= json_object_new_array(); - for (int idx=start; idx <= stop; idx++) { - json_object *argJ=LuaPopOneArg (luaState, idx); - if (!argJ) goto OnErrorExit; - json_object_array_add(responseJ, argJ); - } - } - - return responseJ; - - OnErrorExit: - return NULL; -} - - -STATIC int LuaFormatMessage(lua_State* luaState, LuaAfbMessageT action) { - char *message; - - json_object *responseJ= LuaPopArgs(luaState, LUA_FIST_ARG); - - if (!responseJ) { - luaL_error(luaState,"LuaFormatMessage empty message"); - goto OnErrorExit; - } - - // if we have only on argument just return the value. - if (json_object_get_type(responseJ)!=json_type_array || json_object_array_length(responseJ) <2) { - message= (char*)json_object_get_string(responseJ); - goto PrintMessage; - } - - // extract format and push all other parameter on the stack - message = alloca (LUA_MSG_MAX_LENGTH); - const char *format = json_object_get_string(json_object_array_get_idx(responseJ, 0)); - - int arrayIdx=1; - int targetIdx=0; - - for (int idx=0; format[idx] !='\0'; idx++) { - - if (format[idx]=='%' && format[idx] !='\0') { - json_object *slotJ= json_object_array_get_idx(responseJ, arrayIdx); - //if (slotJ) AFB_NOTICE("**** idx=%d slotJ=%s", arrayIdx, json_object_get_string(slotJ)); - - - switch (format[++idx]) { - case 'd': - if (slotJ) targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"%d", json_object_get_int(slotJ)); - else targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"nil"); - arrayIdx++; - break; - case 'f': - if (slotJ) targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"%f", json_object_get_double(slotJ)); - else targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"nil"); - arrayIdx++; - break; - - case'%': - message[targetIdx]='%'; - targetIdx++; - break; - - case 's': - default: - if (slotJ) targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"%s", json_object_get_string(slotJ)); - else targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"nil"); - arrayIdx++; - } - - } else { - if (targetIdx >= LUA_MSG_MAX_LENGTH) { - AFB_WARNING ("LuaFormatMessage: message[%s] owerverflow LUA_MSG_MAX_LENGTH=%d", format, LUA_MSG_MAX_LENGTH); - targetIdx --; // move backward for EOL - break; - } else { - message[targetIdx++] = format[idx]; - } - } - } - message[targetIdx]='\0'; - -PrintMessage: - switch (action) { - case AFB_MSG_WARNING: - AFB_WARNING (message); - break; - case AFB_MSG_NOTICE: - AFB_NOTICE (message); - break; - case AFB_MSG_DEBUG: - AFB_DEBUG (message); - break; - case AFB_MSG_INFO: - AFB_INFO (message); - break; - case AFB_MSG_ERROR: - default: - AFB_ERROR (message); - } - return 0; // nothing return to lua - - OnErrorExit: // on argument to return (the error message) - return 1; -} - -STATIC int LuaPrintInfo(lua_State* luaState) { - int err=LuaFormatMessage (luaState, AFB_MSG_INFO); - return err; -} - -STATIC int LuaPrintError(lua_State* luaState) { - int err=LuaFormatMessage (luaState, AFB_MSG_ERROR); - return err; // no value return -} - -STATIC int LuaPrintWarning(lua_State* luaState) { - int err=LuaFormatMessage (luaState, AFB_MSG_WARNING); - return err; -} - -STATIC int LuaPrintNotice(lua_State* luaState) { - int err=LuaFormatMessage (luaState, AFB_MSG_NOTICE); - return err; -} - -STATIC int LuaPrintDebug(lua_State* luaState) { - int err=LuaFormatMessage (luaState, AFB_MSG_DEBUG); - return err; -} - -STATIC int LuaAfbSuccess(lua_State* luaState) { - LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG); - if (!afbContext) goto OnErrorExit; - - // ignore context argument - json_object *responseJ= LuaPopArgs(luaState, LUA_FIST_ARG+1); - if (responseJ == JSON_ERROR) return 1; - - afb_req_success(afbContext->request, responseJ, NULL); - - LuaCtxFree(afbContext); - return 0; - - OnErrorExit: - lua_error(luaState); - return 1; -} - -STATIC int LuaAfbFail(lua_State* luaState) { - LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG); - if (!afbContext) goto OnErrorExit; - - json_object *responseJ= LuaPopArgs(luaState, LUA_FIST_ARG+1); - if (responseJ == JSON_ERROR) return 1; - - afb_req_fail(afbContext->request, afbContext->info, json_object_get_string(responseJ)); - - LuaCtxFree(afbContext); - return 0; - - OnErrorExit: - lua_error(luaState); - return 1; -} - -STATIC void LuaAfbServiceCB(void *handle, int iserror, struct json_object *responseJ) { - LuaCallServiceT *contextCB= (LuaCallServiceT*)handle; - int count=1; - - lua_getglobal(luaState, contextCB->callback); - - // push error status & response - lua_pushboolean(luaState, iserror); - count+= LuaPushArgument(responseJ); - count+= LuaPushArgument(contextCB->context); - - int err=lua_pcall(luaState, count, LUA_MULTRET, 0); - if (err) { - AFB_ERROR ("LUA-SERICE-CB:FAIL response=%s err=%s", json_object_get_string(responseJ), lua_tostring(luaState,-1) ); - } - - free (contextCB); -} - - -STATIC int LuaAfbService(lua_State* luaState) { - int count = lua_gettop(luaState); - - // note: argument start at 2 because of AFB: table - if (count <5 || !lua_isstring(luaState, 2) || !lua_isstring(luaState, 3) || !lua_istable(luaState, 4) || !lua_isstring(luaState, 5)) { - lua_pushliteral (luaState, "LuaAfbServiceCall-Syntax is AFB:service_call (api, verb, query, callback, handle ...."); - goto OnErrorExit; - } - - // get api/verb+query - const char *api = lua_tostring(luaState,2); - const char *verb= lua_tostring(luaState,3); - json_object *queryJ= LuaTableToJson(luaState, 4); - if (queryJ == JSON_ERROR) return 1; - - LuaCallServiceT *contextCB = calloc (1, sizeof(LuaCallServiceT)); - contextCB->callback= lua_tostring(luaState, 5); - contextCB->context = LuaPopArgs(luaState, 6); - - afb_service_call(api, verb, queryJ, LuaAfbServiceCB, contextCB); - - return 0; // no value return - - OnErrorExit: - lua_error(luaState); - return 1; -} - -STATIC int LuaAfbServiceSync(lua_State* luaState) { - int count = lua_gettop(luaState); - json_object *responseJ; - - // note: argument start at 2 because of AFB: table - if (count <3 || !lua_isstring(luaState, 2) || !lua_isstring(luaState, 3) || !lua_istable(luaState, 4)) { - lua_pushliteral (luaState, "LuaAfbServiceSync-Syntax is error,data=AFB:service_call (api, verb, query)"); - goto OnErrorExit; - } - - // get api/verb+query - const char *api = lua_tostring(luaState,2); - const char *verb= lua_tostring(luaState,3); - json_object *queryJ= LuaTableToJson(luaState, 4); - - int iserror=afb_service_call_sync (api, verb, queryJ, &responseJ); - - // push error status & response - count=1; lua_pushboolean(luaState, iserror); - count+= LuaPushArgument(responseJ); - - return count; // return count values - - OnErrorExit: - lua_error(luaState); - return 1; -} - -STATIC int LuaAfbEventPush(lua_State* luaState) { - LuaAfbEvent *afbevt; - int index; - - // if no private event handle then use default binding event - if (lua_islightuserdata(luaState, LUA_FIST_ARG)) { - afbevt = (LuaAfbEvent*) lua_touserdata(luaState, LUA_FIST_ARG); - index=LUA_FIST_ARG+1; - } else { - index=LUA_FIST_ARG; - afbevt=luaDefaultEvt; - } - - if (!afb_event_is_valid(afbevt->event)) { - lua_pushliteral (luaState, "LuaAfbMakePush-Fail invalid event"); - goto OnErrorExit; - } - - json_object *ctlEventJ= LuaTableToJson(luaState, index); - if (!ctlEventJ) { - lua_pushliteral (luaState, "LuaAfbEventPush-Syntax is AFB:signal ([evtHandle], {lua table})"); - goto OnErrorExit; - } - - int done = afb_event_push(afbevt->event, ctlEventJ); - if (!done) { - lua_pushliteral (luaState, "LuaAfbEventPush-Fail No Subscriber to event"); - AFB_ERROR ("LuaAfbEventPush-Fail name subscriber event=%s count=%d", afbevt->name, afbevt->count); - goto OnErrorExit; - } - afbevt->count++; - return 0; - - OnErrorExit: - lua_error(luaState); - return 1; -} - -STATIC int LuaAfbEventSubscribe(lua_State* luaState) { - LuaAfbEvent *afbevt; - - LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG); - if (!afbContext) { - lua_pushliteral (luaState, "LuaAfbEventSubscribe-Fail Invalid request handle"); - goto OnErrorExit; - } - - // if no private event handle then use default binding event - if (lua_islightuserdata(luaState, LUA_FIST_ARG+1)) { - afbevt = (LuaAfbEvent*) lua_touserdata(luaState, LUA_FIST_ARG+1); - } else { - afbevt=luaDefaultEvt; - } - - if (!afb_event_is_valid(afbevt->event)) { - lua_pushliteral (luaState, "LuaAfbMakePush-Fail invalid event handle"); - goto OnErrorExit; - } - - int err = afb_req_subscribe(afbContext->request, afbevt->event); - if (err) { - lua_pushliteral (luaState, "LuaAfbEventSubscribe-Fail No Subscriber to event"); - AFB_ERROR ("LuaAfbEventPush-Fail name subscriber event=%s count=%d", afbevt->name, afbevt->count); - goto OnErrorExit; - } - afbevt->count++; - return 0; - - OnErrorExit: - lua_error(luaState); - return 1; -} - -STATIC int LuaAfbEventMake(lua_State* luaState) { - int count = lua_gettop(luaState); - LuaAfbEvent *afbevt=calloc(1,sizeof(LuaAfbEvent)); - - if (count != LUA_FIST_ARG || !lua_isstring(luaState, LUA_FIST_ARG)) { - lua_pushliteral (luaState, "LuaAfbEventMake-Syntax is evtHandle= AFB:event ('myEventName')"); - goto OnErrorExit; - } - - // event name should be the only argument - afbevt->name= strdup (lua_tostring(luaState,LUA_FIST_ARG)); - - // create a new binder event - afbevt->event = afb_daemon_make_event(afbevt->name); - if (!afb_event_is_valid(afbevt->event)) { - lua_pushliteral (luaState, "LuaAfbEventMake-Fail to Create Binder event"); - goto OnErrorExit; - } - - // push event handler as a LUA opaque handle - lua_pushlightuserdata(luaState, afbevt); - return 1; - - OnErrorExit: - lua_error(luaState); - return 1; -} - -// Function call from LUA when lua2c plugin L2C is used -PUBLIC int Lua2cWrapper(lua_State* luaState, char *funcname, Lua2cFunctionT callback, void *context) { - - json_object *argsJ= LuaPopArgs(luaState, LUA_FIST_ARG); - int response = (*callback) (funcname, argsJ, context); - - // push response to LUA - lua_pushinteger(luaState, response); - return 1; -} - -// Generated some fake event based on watchdog/counter -PUBLIC int LuaCallFunc (DispatchActionT *action, json_object *queryJ) { - - int err, count; - - json_object* argsJ = action->argsJ; - const char* func = action->call; - - // load function (should exist in CONTROL_PATH_LUA - lua_getglobal(luaState, func); - - // push argsJ on the stack - count=0; - if (!argsJ) { - lua_pushnil(luaState); - count++; - } else { - count+= LuaPushArgument (argsJ); - } - - // push queryJ on the stack - if (!queryJ) { - lua_pushnil(luaState); - count++; - } else { - count+= LuaPushArgument (queryJ); - } - - // effectively exec LUA script code - err=lua_pcall(luaState, count, 1, 0); - if (err) { - AFB_ERROR("LuaCallFunc Fail calling %s error=%s", func, lua_tostring(luaState,-1)); - goto OnErrorExit; - } - - // return LUA script value - int rc= (int)lua_tointeger(luaState, -1); - return rc; - - OnErrorExit: - return -1; -} - - -// Execute LUA code from received API request -STATIC void LuaDoAction (LuaDoActionT action, afb_req request) { - - int err, count=0; - - json_object* queryJ = afb_req_json(request); - - switch (action) { - - case LUA_DOSTRING: { - const char *script = json_object_get_string(queryJ); - err=luaL_loadstring(luaState, script); - if (err) { - AFB_ERROR ("LUA-DO-COMPILE:FAIL String=%s err=%s", script, lua_tostring(luaState,-1) ); - goto OnErrorExit; - } - // Push AFB client context on the stack - LuaAfbContextT *afbContext= LuaCtxPush(luaState, request,NULL,script); - if (!afbContext) goto OnErrorExit; - - break; - } - - case LUA_DOCALL: { - const char *func; - json_object *argsJ=NULL; - - err= wrap_json_unpack (queryJ, "{s:s, s?o !}", "func", &func, "args", &argsJ); - if (err) { - AFB_ERROR ("LUA-DOCALL-SYNTAX missing func|args query=%s", json_object_get_string(queryJ)); - goto OnErrorExit; - } - - // load function (should exist in CONTROL_PATH_LUA - lua_getglobal(luaState, func); - - // Push AFB client context on the stack - LuaAfbContextT *afbContext= LuaCtxPush(luaState, request, NULL, func); - if (!afbContext) goto OnErrorExit; - - // push query on the stack - if (!argsJ) { - lua_pushnil(luaState); - count++; - } else { - count+= LuaPushArgument (argsJ); - } - - break; - } - - case LUA_DOSCRIPT: { // Fulup need to fix argument passing - const char *script; - char*func=NULL; - char *filename; char*fullpath; - char luaScriptPath[CONTROL_MAXPATH_LEN]; - json_object *argsJ=NULL; - int index; - - // scan luascript search path once - static json_object *luaScriptPathJ =NULL; - - err= wrap_json_unpack (queryJ, "{s:s, s?s s?o !}", "script", &script,"func", &func, "args", &argsJ); - if (err) { - AFB_ERROR ("LUA-DOSCRIPT-SYNTAX:missing script|(args,arg) query=%s", json_object_get_string(queryJ)); - goto OnErrorExit; - } - - // search for filename=script in CONTROL_LUA_PATH - if (!luaScriptPathJ) luaScriptPathJ= ScanForConfig(CONTROL_LUA_PATH , CTL_SCAN_RECURSIVE,CONTROL_DOSCRIPT_PRE "-", script); - for (index=0; index < json_object_array_length(luaScriptPathJ); index++) { - json_object *entryJ=json_object_array_get_idx(luaScriptPathJ, index); - - err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "fullpath", &fullpath,"filename", &filename); - if (err) { - AFB_ERROR ("LUA-DOSCRIPT-SCAN:HOOPs invalid config file path = %s", json_object_get_string(entryJ)); - goto OnErrorExit; - } - - if (index > 0) AFB_WARNING("LUA-DOSCRIPT-SCAN:Ignore second script=%s path=%s", filename, fullpath); - else { - strncpy (luaScriptPath, fullpath, sizeof(luaScriptPath)); - strncat (luaScriptPath, "/", sizeof(luaScriptPath)); - strncat (luaScriptPath, filename, sizeof(luaScriptPath)); - } - } - - err= luaL_loadfile(luaState, luaScriptPath); - if (err) { - AFB_ERROR ("LUA-DOSCRIPT HOOPs Error in LUA loading scripts=%s err=%s", luaScriptPath, lua_tostring(luaState,-1)); - goto OnErrorExit; - } - - // script was loaded we need to parse to make it executable - err=lua_pcall(luaState, 0, 0, 0); - if (err) { - AFB_ERROR ("LUA-DOSCRIPT:FAIL to load %s", luaScriptPath); - goto OnErrorExit; - } - - // if no func name given try to deduct from filename - if (!func) func= (char*)GetMidleName(filename); - if (!func) { - AFB_ERROR ("LUA-DOSCRIPT:FAIL to deduct funcname from %s", filename); - goto OnErrorExit; - } - - // load function (should exist in CONTROL_PATH_LUA - lua_getglobal(luaState, func); - - // Push AFB client context on the stack - LuaAfbContextT *afbContext= LuaCtxPush(luaState, request, NULL, func); - if (!afbContext) goto OnErrorExit; - - // push function arguments - if (!argsJ) { - lua_pushnil(luaState); - count++; - } else { - count+= LuaPushArgument(argsJ); - } - - break; - } - - default: - AFB_ERROR ("LUA-DOSCRIPT-ACTION unknown query=%s", json_object_get_string(queryJ)); - goto OnErrorExit; - } - - // effectively exec LUA code (afb_reply/fail done later from callback) - err=lua_pcall(luaState, count+1, 0, 0); - if (err) { - AFB_ERROR ("LUA-DO-EXEC:FAIL query=%s err=%s", json_object_get_string(queryJ), lua_tostring(luaState,-1)); - goto OnErrorExit; - } - return; - - OnErrorExit: - afb_req_fail(request,"LUA:ERROR", lua_tostring(luaState,-1)); - return; -} - -PUBLIC void ctlapi_lua_dostring (afb_req request) { - LuaDoAction (LUA_DOSTRING, request); -} - -PUBLIC void ctlapi_lua_docall (afb_req request) { - LuaDoAction (LUA_DOCALL, request); -} - -PUBLIC void ctlapi_lua_doscript (afb_req request) { - LuaDoAction (LUA_DOSCRIPT, request); -} - -STATIC int LuaTimerClear (lua_State* luaState) { - - // Get Timer Handle - LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG); - if (!afbContext) goto OnErrorExit; - - // retrieve useful information opaque handle - TimerHandleT *timerHandle = (TimerHandleT*)afbContext->handle; - - AFB_NOTICE ("LuaTimerClear timer=%s", timerHandle->label); - TimerEvtStop(timerHandle); - - return 0; //happy end - -OnErrorExit: - return 1; -} -STATIC int LuaTimerGet (lua_State* luaState) { - - // Get Timer Handle - LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG); - if (!afbContext) goto OnErrorExit; - - // retrieve useful information opaque handle - TimerHandleT *timerHandle = (TimerHandleT*)afbContext->handle; - - // create response as a JSON object - json_object *responseJ= json_object_new_object(); - json_object_object_add(responseJ,"label", json_object_new_string(timerHandle->label)); - json_object_object_add(responseJ,"delay", json_object_new_int(timerHandle->delay)); - json_object_object_add(responseJ,"count", json_object_new_int(timerHandle->count)); - - // return JSON object as Lua table - int count=LuaPushArgument(responseJ); - - // free json object - json_object_put(responseJ); - - return count; // return argument - -OnErrorExit: - return 0; -} - -// Timer Callback - -// Set timer -STATIC int LuaTimerSetCB (void *handle) { - LuaCallServiceT *contextCB =(LuaCallServiceT*) handle; - TimerHandleT *timerHandle = (TimerHandleT*) contextCB->handle; - int count; - - // push timer handle and user context on Lua stack - lua_getglobal(luaState, contextCB->callback); - - // Push timer handle - LuaAfbContextT *afbContext= LuaCtxPush(luaState, NULL_AFBREQ, contextCB->handle, timerHandle->label); - if (!afbContext) goto OnErrorExit; - count=1; - - // Push user Context - count+= LuaPushArgument(contextCB->context); - - int err=lua_pcall(luaState, count, LUA_MULTRET, 0); - if (err) { - AFB_ERROR ("LUA-TIMER-CB:FAIL response=%s err=%s", json_object_get_string(contextCB->context), lua_tostring(luaState,-1)); - goto OnErrorExit; - } - - // get return parameter - if (!lua_isboolean(luaState, -1)) { - return (lua_toboolean(luaState, -1)); - } - - // timer last run free context resource - if (timerHandle->count == 1) { - LuaCtxFree(afbContext); - } - return 0; // By default we are happy - - OnErrorExit: - return 1; // stop timer -} - -STATIC int LuaTimerSet(lua_State* luaState) { - const char *label=NULL, *info=NULL; - int delay=0, count=0; - - json_object *timerJ = LuaPopOneArg(luaState, LUA_FIST_ARG); - const char *callback = lua_tostring(luaState, LUA_FIST_ARG + 1); - json_object *contextJ = LuaPopOneArg(luaState, LUA_FIST_ARG + 2); - - if (lua_gettop(luaState) != LUA_FIST_ARG+2 || !timerJ || !callback || !contextJ) { - lua_pushliteral(luaState, "LuaTimerSet-Syntax timerset (timerT, 'callback', contextT)"); - goto OnErrorExit; - } - - int err = wrap_json_unpack(timerJ, "{ss, s?s si, si !}", "label", &label, "info", &info, "delay", &delay, "count", &count); - if (err) { - lua_pushliteral(luaState, "LuaTimerSet-Syntax timerT={label:xxx delay:ms, count:xx}"); - goto OnErrorExit; - } - - // everything look fine create timer structure - TimerHandleT *timerHandle = malloc (sizeof (TimerHandleT)); - timerHandle->delay=delay; - timerHandle->count=count; - timerHandle->label=label; - - // Allocate handle to store context and callback - LuaCallServiceT *contextCB = calloc (1, sizeof(LuaCallServiceT)); - contextCB->callback= callback; - contextCB->context = contextJ; - contextCB->handle = timerHandle; - - // fire timer - TimerEvtStart (timerHandle, LuaTimerSetCB, contextCB); - - return 0; // Happy No Return Function - -OnErrorExit: - lua_error(luaState); - return 1; // return error code -} - -// Register a new L2c list of LUA user plugin commands -PUBLIC void LuaL2cNewLib(const char *label, luaL_Reg *l2cFunc, int count) { - // luaL_newlib(luaState, l2cFunc); macro does not work with pointer :( - luaL_checkversion(luaState); - lua_createtable(luaState, 0, count+1); - luaL_setfuncs(luaState,l2cFunc,0); - lua_setglobal(luaState, label); -} - -static const luaL_Reg afbFunction[] = { - {"timerclear", LuaTimerClear}, - {"timerget" , LuaTimerGet}, - {"timerset" , LuaTimerSet}, - {"notice" , LuaPrintNotice}, - {"info" , LuaPrintInfo}, - {"warning" , LuaPrintWarning}, - {"debug" , LuaPrintDebug}, - {"error" , LuaPrintError}, - {"callsync" , LuaAfbServiceSync}, - {"service" , LuaAfbService}, - {"success" , LuaAfbSuccess}, - {"fail" , LuaAfbFail}, - {"subscribe" , LuaAfbEventSubscribe}, - {"evtmake" , LuaAfbEventMake}, - {"evtpush" , LuaAfbEventPush}, - - {NULL, NULL} /* sentinel */ -}; - -// Create Binding Event at Init -PUBLIC int LuaLibInit () { - int err, index; - - // search for default policy config file - char fullprefix[CONTROL_MAXPATH_LEN]; - strncpy (fullprefix, CONTROL_CONFIG_PRE "-", sizeof(fullprefix)); - strncat (fullprefix, GetBinderName(), sizeof(fullprefix)); - strncat (fullprefix, "-", sizeof(fullprefix)); - - json_object *luaScriptPathJ = ScanForConfig(CONTROL_LUA_PATH , CTL_SCAN_RECURSIVE, fullprefix, "lua"); - - // open a new LUA interpretor - luaState = luaL_newstate(); - if (!luaState) { - AFB_ERROR ("LUA_INIT: Fail to open lua interpretor"); - goto OnErrorExit; - } - - // load auxiliary libraries - luaL_openlibs(luaState); - - // redirect print to AFB_NOTICE - luaL_newlib(luaState, afbFunction); - lua_setglobal(luaState, "AFB"); - - // create default lua event to send test pause/resume - luaDefaultEvt=calloc(1,sizeof(LuaAfbEvent)); - luaDefaultEvt->name=CONTROL_LUA_EVENT; - luaDefaultEvt->event = afb_daemon_make_event(CONTROL_LUA_EVENT); - if (!afb_event_is_valid(luaDefaultEvt->event)) { - AFB_ERROR ("POLCTL_INIT: Cannot register lua-events=%s ", CONTROL_LUA_EVENT); - goto OnErrorExit;; - } - - // load+exec any file found in LUA search path - for (index=0; index < json_object_array_length(luaScriptPathJ); index++) { - json_object *entryJ=json_object_array_get_idx(luaScriptPathJ, index); - - char *filename; char*fullpath; - err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "fullpath", &fullpath,"filename", &filename); - if (err) { - AFB_ERROR ("LUA-INIT HOOPs invalid config file path = %s", json_object_get_string(entryJ)); - goto OnErrorExit; - } - - char filepath[CONTROL_MAXPATH_LEN]; - strncpy(filepath, fullpath, sizeof(filepath)); - strncat(filepath, "/", sizeof(filepath)); - strncat(filepath, filename, sizeof(filepath)); - err= luaL_loadfile(luaState, filepath); - if (err) { - AFB_ERROR ("LUA-LOAD HOOPs Error in LUA loading scripts=%s err=%s", filepath, lua_tostring(luaState,-1)); - goto OnErrorExit; - } - - // exec/compil script - err = lua_pcall(luaState, 0, 0, 0); - if (err) { - AFB_ERROR ("LUA-LOAD HOOPs Error in LUA exec scripts=%s err=%s", filepath, lua_tostring(luaState,-1)); - goto OnErrorExit; - } - } - - // no policy config found remove control API from binder - if (index == 0) { - AFB_WARNING ("POLICY-INIT:WARNING No Control LUA file in path=[%s]", CONTROL_LUA_PATH); - } - - AFB_DEBUG ("Audio control-LUA Init Done"); - return 0; - - OnErrorExit: - return 1; -} - \ No newline at end of file diff --git a/Controler-afb/ctl-plugin-sample.c b/Controler-afb/ctl-plugin-sample.c deleted file mode 100644 index 1d24487..0000000 --- a/Controler-afb/ctl-plugin-sample.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2016 "IoT.bzh" - * Author Fulup Ar Foll - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Sample plugin for Controller - */ - - -#define _GNU_SOURCE -#include -#include - -#include "ctl-binding.h" - -#define MY_PLUGIN_MAGIC 987654321 - -typedef struct { - int magic; - int count; -} MyPluginCtxT; - -STATIC const char* jsonToString (json_object *valueJ) { - const char *value; - if (valueJ) - value=json_object_get_string(valueJ); - else - value="NULL"; - - return value; -} - -// Declare this sharelib as a Controller Plugin -CTLP_REGISTER("MyCtlSamplePlugin"); - -// Call at initialisation time -PUBLIC CTLP_ONLOAD(label, version, info) { - MyPluginCtxT *pluginCtx= (MyPluginCtxT*)calloc (1, sizeof(MyPluginCtxT)); - pluginCtx->magic = MY_PLUGIN_MAGIC; - pluginCtx->count = -1; - - AFB_NOTICE ("CONTROLER-PLUGIN-SAMPLE:Onload label=%s version=%s info=%s", label, info, version); - return (void*)pluginCtx; -} - -PUBLIC CTLP_CAPI (SamplePolicyInit, label, argsJ, queryJ, context) { - MyPluginCtxT *pluginCtx= (MyPluginCtxT*)context; - if (!context || pluginCtx->magic != MY_PLUGIN_MAGIC) { - AFB_ERROR("CONTROLER-PLUGIN-SAMPLE:SamplePolicyInit (Hoops) Invalid Sample Plugin Context"); - return -1; - }; - - pluginCtx->count = 0; - AFB_NOTICE ("CONTROLER-PLUGIN-SAMPLE:Init label=%s args=%s\n", label, jsonToString(argsJ)); - return 0; -} - -PUBLIC CTLP_CAPI (sampleControlMultimedia,label, argsJ,queryJ,context) { - MyPluginCtxT *pluginCtx= (MyPluginCtxT*)context; - - if (!context || pluginCtx->magic != MY_PLUGIN_MAGIC) { - AFB_ERROR("CONTROLER-PLUGIN-SAMPLE:sampleControlMultimedia (Hoops) Invalid Sample Plugin Context"); - return -1; - }; - pluginCtx->count++; - AFB_NOTICE ("CONTROLER-PLUGIN-SAMPLE:sampleControlMultimedia SamplePolicyCount action=%s args=%s query=%s count=%d" - , label, jsonToString(argsJ), jsonToString(queryJ), pluginCtx->count); - return 0; -} - -PUBLIC CTLP_CAPI (sampleControlNavigation, label, argsJ, queryJ, context) { - MyPluginCtxT *pluginCtx= (MyPluginCtxT*)context; - - if (!context || pluginCtx->magic != MY_PLUGIN_MAGIC) { - AFB_ERROR("CONTROLER-PLUGIN-SAMPLE:sampleControlNavigation (Hoops) Invalid Sample Plugin Context"); - return -1; - }; - pluginCtx->count++; - AFB_NOTICE ("CONTROLER-PLUGIN-SAMPLE:sampleControlNavigation SamplePolicyCount action=%s args=%s query=%s count=%d" - ,label, jsonToString(argsJ), jsonToString(queryJ), pluginCtx->count); - return 0; -} - -PUBLIC CTLP_CAPI (SampleControlEvent, label, argsJ, queryJ, context) { - MyPluginCtxT *pluginCtx= (MyPluginCtxT*)context; - - if (!context || pluginCtx->magic != MY_PLUGIN_MAGIC) { - AFB_ERROR("CONTROLER-PLUGIN-SAMPLE:cousampleControlMultimediant (Hoops) Invalid Sample Plugin Context"); - return -1; - }; - pluginCtx->count++; - AFB_NOTICE ("CONTROLER-PLUGIN-SAMPLE:sampleControlMultimedia SamplePolicyCount action=%s args=%s query=%s count=%d" - ,label, jsonToString(argsJ), jsonToString(queryJ), pluginCtx->count); - return 0; -} - -// This function is a LUA function. Lua2CHelloWorld label should be declare in the "onload" section of JSON config file -PUBLIC CTLP_LUA2C (Lua2cHelloWorld1, label, argsJ, context) { - MyPluginCtxT *pluginCtx= (MyPluginCtxT*)context; - - if (!context || pluginCtx->magic != MY_PLUGIN_MAGIC) { - AFB_ERROR("CONTROLER-PLUGIN-SAMPLE:Lua2cHelloWorld1 (Hoops) Invalid Sample Plugin Context"); - return -1; - }; - pluginCtx->count++; - AFB_NOTICE ("CONTROLER-PLUGIN-SAMPLE:Lua2cHelloWorld1 SamplePolicyCount action=%s args=%s count=%d" - ,label, jsonToString(argsJ), pluginCtx->count); - return 0; -} - -// This function is a LUA function. Lua2CHelloWorld label should be declare in the "onload" section of JSON config file -PUBLIC CTLP_LUA2C (Lua2cHelloWorld2, label, argsJ, context) { - MyPluginCtxT *pluginCtx= (MyPluginCtxT*)context; - - if (!context || pluginCtx->magic != MY_PLUGIN_MAGIC) { - AFB_ERROR("CONTROLER-PLUGIN-SAMPLE:Lua2cHelloWorld2 (Hoops) Invalid Sample Plugin Context"); - return -1; - }; - pluginCtx->count++; - AFB_NOTICE ("CONTROLER-PLUGIN-SAMPLE:Lua2cHelloWorld2 SamplePolicyCount action=%s args=%s count=%d" - ,label, jsonToString(argsJ), pluginCtx->count); - return 0; -} diff --git a/Controler-afb/ctl-policy.c b/Controler-afb/ctl-policy.c deleted file mode 100644 index e9798b3..0000000 --- a/Controler-afb/ctl-policy.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2016 "IoT.bzh" - * Author Fulup Ar Foll - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, something express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include - -#include "wrap-json.h" -#include "ctl-binding.h" - -STATIC PolicyCtlConfigT *ctlHandle = NULL; - -// List Avaliable Configuration Files -STATIC json_object* ScanForConfig (char* searchPath) { - json_object *responseJ; - DIR *dirHandle; - char *dirPath; - char* dirList= strdup(searchPath); - - responseJ = json_object_new_array(); - for (dirPath= strtok(dirList, ":"); dirPath && *dirPath; dirPath=strtok(NULL,":")) { - struct dirent *dirEnt; - - dirHandle = opendir (dirPath); - if (!dirHandle) { - AFB_NOTICE ("CONFIG-SCANNING dir=%s not readable", dirPath); - continue; - } - - AFB_NOTICE ("CONFIG-SCANNING:ctl_listconfig scanning: %s", dirPath); - while ((dirEnt = readdir(dirHandle)) != NULL) { - // Unknown type is accepted to support dump filesystems - if (dirEnt->d_type == DT_REG || dirEnt->d_type == DT_UNKNOWN) { - struct json_object *pathJ = json_object_new_object(); - json_object_object_add(pathJ, "dirpath", json_object_new_string(dirPath)); - json_object_object_add(pathJ, "filename", json_object_new_string(dirEnt->d_name)); - json_object_array_add(responseJ, pathJ); - } - } - } - - free (dirList); - return (responseJ); -} - -PUBLIC void ctlapi_authorize (PolicyCtlEnumT control, afb_req request) { - json_object*tmpJ; - - json_object* queryJ= afb_req_json(request); - int done=json_object_object_get_ex(queryJ, "closing", &tmpJ); - if (done) return; - -} - - -// List Avaliable Configuration Files -PUBLIC void ctlapi_config (struct afb_req request) { - json_object*tmpJ; - char *dirList; - - json_object* queryJ = afb_req_json(request); - if (queryJ && json_object_object_get_ex (queryJ, "cfgpath" , &tmpJ)) { - dirList = strdup (json_object_get_string(tmpJ)); - } else { - dirList = strdup (CONTROL_CONFIG_PATH); - AFB_NOTICE ("CONFIG-MISSING: use default CONTROL_CONFIG_PATH=%s", CONTROL_CONFIG_PATH); - } - - // get list of config file - struct json_object *responseJ = ScanForConfig(dirList); - - if (json_object_array_length(responseJ) == 0) { - afb_req_fail(request, "CONFIGPATH:EMPTY", "No Config Found in CONTROL_CONFIG_PATH"); - } else { - afb_req_success(request, responseJ, NULL); - } - - return; -} - -STATIC PolicyActionT **PolicyLoadActions (json_object *actionsJ) { - int err; - PolicyActionT ** actions; - - // unpack individual action object - int actionUnpack (json_object *actionJ, PolicyActionT *action) { - - err= wrap_json_unpack(actionJ, "{ss,s?s,s?s,s?s,s?s,s?s !}" - , "label",&action->label, "info",&action->info, "callback",&action->callback, "query",&queryJ, "api",&action->api, "verb", &action->verb); - if (err) { - AFB_ERROR ("POLICY-LOAD-ACTION Missing something label|info|callback|api|verb|query in %s", json_object_get_string(actionJ)); - return -1; - } - if (!action->callback || !(action->api && action->verb)) { - AFB_ERROR ("POLICY-LOAD-ACTION Missing something callback|(api+verb) in %s", json_object_get_string(actionJ)); - return -1; - } - return 0; - }; - - // action array is close with a nullvalue; - if (json_object_get_type(actionsJ) == json_type_array) { - int count = json_object_array_length(actionsJ); - actions = calloc (count+1, sizeof(PolicyActionT)); - - for (int idx=0; idx < count; idx++) { - json_object *actionJ = json_object_array_get_idx(actionsJ, idx); - err = actionUnpack (actionJ, &actions[idx]); - if (err) goto OnErrorExit; - } - - } else { - actions = calloc (2, sizeof(PolicyActionT)); - err = actionUnpack (actionsJ, &actions[0]); - if (err) goto OnErrorExit; - } - - return actions; - - OnErrorExit: - return NULL; - -} - -// load control policy from file using json_unpack https://jansson.readthedocs.io/en/2.9/apiref.html#parsing-and-validating-values -STATIC PolicyCtlConfigT *PolicyLoadConfig (const char* filepath) { - json_object *policyConfigJ, *ignoreJ, *actionsJ; - PolicyCtlConfigT *policyConfig = calloc (1, sizeof(PolicyCtlConfigT)); - int err; - - // Load JSON file - policyConfigJ= json_object_from_file(filepath); - if (!policyConfigJ) goto OnErrorExit; - - json_object *metadataJ, *onloadJ, *controlsJ, *eventsJ; - err= wrap_json_unpack(policyConfigJ, "{s?o,so,s?o,so,so !}", "$schema", &ignoreJ, "metadata",&metadataJ, "onload",&onloadJ, "controls",&controlsJ, "events",&eventsJ); - if (err) { - AFB_ERROR ("POLICY-LOAD-ERRROR Missing something metadata|onload|controls|events in %s", json_object_get_string(policyConfigJ)); - goto OnErrorExit; - } - - PolicyHandleT *policyHandle = calloc (1, sizeof(PolicyHandleT)); - err= wrap_json_unpack(metadataJ, "{so,s?s,s?s !}", "label", &policyHandle->label, "info",&policyHandle->info, "version",&policyHandle->version); - if (err) { - AFB_ERROR ("POLICY-LOAD-CONFIG Missing something label|info|version in %s", json_object_get_string(metadataJ)); - goto OnErrorExit; - } - - if (onloadJ) { - err= wrap_json_unpack(onloadJ, "{s?o,s?s,s?s !}", "info",&ignoreJ, "label",&ignoreJ, "actions",&actionsJ); - if (err) { - AFB_ERROR ("POLICY-LOAD-CONFIG Missing something label|info|plugin|actions in %s", json_object_get_string(metadataJ)); - goto OnErrorExit; - } - policyConfig->onload = PolicyLoadActions (actionsJ); - } - - return policyControl; - -OnErrorExit: - return NULL; -} - -// Load default config file at init -PUBLIC int PolicyInit () { - int index, err; - - - // search for default policy config file - json_object* responseJ = ScanForConfig(CONTROL_CONFIG_PATH); - - for (index=0; index < json_object_array_length(responseJ); index++) { - json_object *entryJ=json_object_array_get_idx(responseJ, index); - - char *filename; char*dirpath; - err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "dirpath", &dirpath,"filename", &filename); - if (err) { - AFB_ERROR ("POLICY-INIT HOOPs invalid config file path = %s", json_object_get_string(entryJ)); - goto OnErrorExit; - } - - if (strcasestr(filename, CONTROL_CONFIG_FILE)) { - char filepath[255]; - strncpy(filepath, dirpath, sizeof(filepath)); - strncat(filepath, "/", sizeof(filepath)); - strncat(filepath, filename, sizeof(filepath)); - ctlHandle = PolicyLoadConfig (filepath); - if (!ctlHandle) goto OnErrorExit; - break; - } - } - - // no policy config found remove control API from binder - if (index == 0) goto OnErrorExit; - - AFB_NOTICE ("SUCCES: Audio Control Policy Init"); - return 0; - -OnErrorExit: - AFB_NOTICE ("ERROR: Audio Control Policy Init"); - return 1; -} - - - diff --git a/Controler-afb/ctl-timer.c b/Controler-afb/ctl-timer.c deleted file mode 100644 index f7ee6f0..0000000 --- a/Controler-afb/ctl-timer.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2016 "IoT.bzh" - * Author Fulup Ar Foll - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include - -#include "ctl-binding.h" - -#define DEFAULT_PAUSE_DELAY 3000 -#define DEFAULT_TEST_COUNT 1 -typedef struct { - int value; - const char *label; -} AutoTestCtxT; - -static afb_event afbevt; - -STATIC int TimerNext (sd_event_source* source, uint64_t timer, void* handle) { - TimerHandleT *timerHandle = (TimerHandleT*) handle; - int done; - uint64_t usec; - - // Rearm timer if needed - timerHandle->count --; - if (timerHandle->count == 0) { - sd_event_source_unref(source); - free (handle); - return 0; - } - else { - // otherwise validate timer for a new run - sd_event_now(afb_daemon_get_event_loop(), CLOCK_MONOTONIC, &usec); - sd_event_source_set_enabled(source, SD_EVENT_ONESHOT); - sd_event_source_set_time(source, usec + timerHandle->delay*1000); - } - - done= timerHandle->callback(timerHandle->context); - if (!done) goto OnErrorExit; - - return 0; - -OnErrorExit: - AFB_WARNING("TimerNext Callback Fail Tag=%s", timerHandle->label); - return -1; -} - -PUBLIC void TimerEvtStop(TimerHandleT *timerHandle) { - - sd_event_source_unref(timerHandle->evtSource); - free (timerHandle); -} - - -PUBLIC void TimerEvtStart(TimerHandleT *timerHandle, timerCallbackT callback, void *context) { - uint64_t usec; - - // populate CB handle - timerHandle->callback=callback; - timerHandle->context=context; - - // set a timer with ~250us accuracy - sd_event_now(afb_daemon_get_event_loop(), CLOCK_MONOTONIC, &usec); - sd_event_add_time(afb_daemon_get_event_loop(), &timerHandle->evtSource, CLOCK_MONOTONIC, usec+timerHandle->delay*1000, 250, TimerNext, timerHandle); -} - -PUBLIC afb_event TimerEvtGet(void) { - return afbevt; -} - - -STATIC int DoSendEvent (void *context) { - AutoTestCtxT *ctx= (AutoTestCtxT*)context; - json_object *ctlEventJ; - - if (ctx->value) ctx->value =0; - else ctx->value =1; - - ctlEventJ = json_object_new_object(); - json_object_object_add(ctlEventJ,"signal", json_object_new_string(ctx->label)); - json_object_object_add(ctlEventJ,"value" , json_object_new_int(ctx->value)); - int done = afb_event_push(afbevt, ctlEventJ); - - AFB_NOTICE ("DoSendEvent {action: '%s', value:%d} status=%d", ctx->label, ctx->value, done); - - return (done); -} - -// Generated some fake event based on watchdog/counter -PUBLIC void ctlapi_timer_test (afb_req request) { - json_object *queryJ, *tmpJ; - TimerHandleT *timerHandle = malloc (sizeof (TimerHandleT)); - AutoTestCtxT *context = calloc (1, sizeof (AutoTestCtxT)); - int done; - - queryJ= afb_req_json(request); - - // Closing call only has one parameter - done=json_object_object_get_ex(queryJ, "closing", &tmpJ); - if (done) return; - - done=json_object_object_get_ex(queryJ, "label", &tmpJ); - if (!done) { - afb_req_fail_f(request, "TEST-LABEL-MISSING", "label is mandatory for event_test"); - goto OnErrorExit; - } - timerHandle->label = strdup(json_object_get_string (tmpJ)); - - json_object_object_get_ex(queryJ, "delay", &tmpJ); - timerHandle->delay = json_object_get_int (tmpJ) * 1000; - if (timerHandle->delay == 0) timerHandle->delay=DEFAULT_PAUSE_DELAY * 1000; - - json_object_object_get_ex(queryJ, "count", &tmpJ); - timerHandle->count = json_object_get_int (tmpJ); - if (timerHandle->count == 0) timerHandle->count=DEFAULT_TEST_COUNT; - - // start a lool timer - TimerEvtStart (timerHandle, DoSendEvent, context); - - afb_req_success(request, NULL, NULL); - return; - - OnErrorExit: - return; -} - -// Create Binding Event at Init -PUBLIC int TimerEvtInit () { - - // create binder event to send test pause/resume - afbevt = afb_daemon_make_event("control"); - if (!afb_event_is_valid(afbevt)) { - AFB_ERROR ("POLCTL_INIT: Cannot register ctl-events"); - return 1; - } - - AFB_DEBUG ("Audio Control-Events Init Done"); - return 0; -} - diff --git a/Controller-afb/CMakeLists.txt b/Controller-afb/CMakeLists.txt new file mode 100644 index 0000000..a9e8e60 --- /dev/null +++ b/Controller-afb/CMakeLists.txt @@ -0,0 +1,84 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: Fulup Ar Foll +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + + +# Generate API-v2 hat from OpenAPI json definition +macro(SET_TARGET_GENSKEL TARGET_NAME API_DEF_NAME) + add_custom_command(OUTPUT ${API_DEF_NAME}.h + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${API_DEF_NAME}.json + COMMAND afb-genskel ${API_DEF_NAME}.json >${API_DEF_NAME}.h + ) + add_custom_target(${API_DEF_NAME}_OPENAPI DEPENDS ${API_DEF_NAME}.h) + add_dependencies(${TARGET_NAME} ${API_DEF_NAME}_OPENAPI) +endmacro(SET_TARGET_GENSKEL) + +# Include LUA only when requested +if(CONTROL_SUPPORT_LUA) + message(STATUS "Notice: LUA Controler Support Selected") + set(CTL_LUA_SOURCE ctl-lua.c) +endif(CONTROL_SUPPORT_LUA) + +# Add target to project dependency list +PROJECT_TARGET_ADD(control-afb) + + # Define project Targets + ADD_LIBRARY(${TARGET_NAME} MODULE ctl-binding.c ctl-timer.c ctl-dispatch.c ${CTL_LUA_SOURCE}) + + # Generate API-v2 hat from OpenAPI json definition + SET_TARGET_GENSKEL(${TARGET_NAME} ctl-apidef) + + # Binder exposes a unique public entry point + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + PREFIX "afb-" + LABELS "BINDING" + LINK_FLAGS ${BINDINGS_LINK_FLAG} + OUTPUT_NAME ${TARGET_NAME} + ) + + # Library dependencies (include updates automatically) + TARGET_LINK_LIBRARIES(${TARGET_NAME} + audio-common + ${link_libraries} + ) + + # installation directory + INSTALL(TARGETS ${TARGET_NAME} + LIBRARY DESTINATION CMAKE_INSTALL_PREFIX ) + +PROJECT_TARGET_ADD(audio-plugin-sample) + + # Define targets + ADD_LIBRARY(${TARGET_NAME} MODULE ctl-plugin-sample.c) + + # Alsa Plugin properties + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + PREFIX ${CTL_PLUGIN_PRE} + SUFFIX ${CTL_PLUGIN_EXT} + OUTPUT_NAME ${TARGET_NAME} + ) + + # Library dependencies (include updates automatically) + TARGET_LINK_LIBRARIES(${TARGET_NAME} + audio-common + ${link_libraries} + ) + + # installation directory + INSTALL(TARGETS ${TARGET_NAME} + LIBRARY DESTINATION ${BINDINGS_INSTALL_DIR}/controler) diff --git a/Controller-afb/README.md b/Controller-afb/README.md new file mode 100644 index 0000000..403f637 --- /dev/null +++ b/Controller-afb/README.md @@ -0,0 +1,333 @@ +Controler AAAA(AGL Advance Audio Controler) and more. +------------------------------------------------------------ + + * Object: Generic Controler to handle Policy,Small Business Logic, Glue in between components, ... + * Status: Release Candidate + * Author: Fulup Ar Foll fulup@iot.bzh + * Date : August-2017 + +## Functionalities: + - Create an application dedicate controller from a JSON config file + - Each controls (eg: navigation, multimedia, ...) is a suite of actions. When all actions succeed control is granted, if one fail control acces is denied. + - Actions can either be: + + Invocation to an other binding API, either internal or external (eg: a policy service, Alsa UCM, ...) + + C routines from a user provider plugin (eg: policy routine, proprietary code, ...) + + Lua script function. Lua provides access to every AGL appfw functionalities and can be extended from C user provided plugins. + +## Installation + - Controler is a native part of AGL Advance Audio Framework but may be used independently with any other service or application binder. + - Dependencies: the only dependencies are audio-common for JSON-WRAP and Filescan-utils capabilities. + - Controler relies on Lua-5.3, when not needed Lua might be removed at compilation time. + +## Config + +Configuration is loaded dynamically during startup time. The controller scans CONTROL_CONFIG_PATH for a file corresponding to pattern +"onload-bindername-xxxxx.json". When controller runs within AAAA binder it searches for "onload-audio-xxxx.json". First file found in the path the loaded +any other files corresponding to the same pather are ignored and only generate a warning. + +Each bloc in the configuration file are defined with + * label: must be provided is used either for debugging or as input for the action (eg: signal name, control name, ...) + * info: optional used for documentation purpose only + +Note by default controller config search path is defined at compilation time, but path might be overloaded with CONTROL_CONFIG_PATH +environment variable. + +### Config is organised in 4 sections: + + * metadata + * onload defines the set of action to be executed at startup time + * control defines the set of controls with corresponding actions + * event define the set of actions to be executed when receiving a given signal + +### Metadata + +As today matadata is only used for documentation purpose. + * label + version mandatory + * info optional + +### OnLoad section + +Defines startup time configuration. Onload may provide multiple initialisation profiles, each with a different label. + * label is mandatory. Label is used to select onload profile at initialisation through DispatchOneOnLoad("onload-label") API; + * info is optional + * plugin provides optional unique plugin name. Plugin should follow "onload-bindername-xxxxx.ctlso" patern + and are search into CONTROL_PLUGIN_PATH. When defined controller will execute user provided function context=CTLP_ONLOAD(label,version,info). + The context returned by this routine is provided back to any C routines call later by the controller. Note that Lua2C function + are prefix in Lua script with plugin label (eg: MyPlug: in following config sample) + * lua2c list of Lua commands shipped with provided plugin. + * require list of binding that should be initialised before the controller starts. Note that some listed requirer binding might be absent, + nevertheless any present binding from this list will be started before controller binding, missing ones generate a warning. + * action the list of action to execute during loadtime. Any failure in action will prevent controller binding from starting. + +### Control section + +Defines a list of controls that are accessible through (api="control", verb="request", control="control-label"). + + * label mandatory + * info optional + * permissions Cynara needed privileges to request this control (same as AppFw-V2) + * action the list of actions + +### Event section + +Defines a list of actions to be executed on event reception. Even can do anything a controller can (change state, +send back signal, ...) eg: if a controller subscribes to vehicule speed, then speed-event may ajust master-volume to speed. + + * label mandatory + * info optional + * action the list of actions + +### Actions Categories + +Controler support tree categories of actions. Each action return a status status where 0=success and 1=failure. + * AppFw API, Provides a generic model to request other bindings. Requested binding can be local (eg: ALSA/UCM) or + external (eg: vehicle signalling). + * api provides requested binding API name + * verb provides verb to requested binding + * args optionally provides a jsonc object for targeted binding API. Note that 'args' are statically defined + in JSON configuration file. Controler client may also provided its own arguments from the query list. Targeted + binding receives both arguments defined in the config file and the argument provided by controller client. + * C-API, when defined in the onload section, the plugin may provided C native API with CTLP-CAPI(apiname, label, args, query, context). + Plugin may also create Lua command with CTLP-Lua2C(LuaFuncName, label, args, query, context). Where args+query are JSONC object + and context the value return from CTLP_ONLOAD function. Any missing value is set to NULL. + * Lua-API, when compiled with Lua option, the controller support action defined directly in Lua script. During "onload" phase the + controller search in CONTROL_Lua_PATH file with pattern "onload-bindername-xxxx.lua". Any file corresponding to this pattern + is automatically loaded. Any function defined in those Lua script can be called through a controller action. Lua functions receive + three parameters (label, args, query). + +Note: Lua added functions systematically prefix. AGL standard AppFw functions are prefixed with AGL: (eg: AGL:notice(), AGL_success(), ...). +User Lua functions added though the plugin and CTLP_Lua2C are prefix with plugin label (eg: MyPlug:HelloWorld1). + +### Avaliable Application Framework Commands + +Each Lua AppFw commands should be prefixed by AFB: + + * AFB:notice ("format", arg1,... argn) LUA table are print directly as json string with '%s'. + AFB:error, AFB:warning, AFB:info, AFB:debug work on the same model. Printed message are limited to 512 characters. + + * AFB:service ('API', 'VERB', {query}, "Lua_Callback_Name", {context}) asynchronous call to an other binding. When empty query/context should be set to '{}' + and not to 'nil'. When 'nil' Lua does not send 'NULL' value but remove arguments to calling stack. WARNING:"Callback" + is the name of the callback as a string and not a pointer to the callback. (If someone as a solution to fix this, please + let me known). Callback is call as LUA "function Alsa_Get_Hal_CB (error, result, context)" where: + * error is a Boolean + * result is the full answer from AppFw (do not forget to extract response) + * context is a copy of the Lua table pas as argument (warning it's a copy not a pointer to original table) + + * error,result=AFB:servsync('API', 'VERB', {query}) Save as previous but for synchronous call. Note that Lua accept multiple + return. AFB:servsync return both the error message and the response as a Lua table. Like for AFB:service user should not + forget to extract response from result. + + * AFB:success(request, response) request is the opaque handle pass when Lua is called from (api="control", verb="docall"). + Response is a Lua table that will be return to client. + + * AFB:fail(request, response) same as for success. Note that LUA generate automatically the error code from Lua function name. + The response is tranformed to a json string before being return to client. + + * EventHandle=AFB:evtmake("MyEventName") Create an event and return the handle as an opaque handle. Note that due to a limitation + of json_object this opaque handle cannot be passed as argument in a callback context. + + * AFB:subscribe(request, MyEventHandle) Subscribe a given client to previously created event. + + * AFB:evtpush (MyEventHandle, MyEventData) Push an event to every subscribed client. MyEventData is a Lua table that will be + send as a json object to corresponding clients. + + * timerHandle=AFB:timerset (MyTimer, "Timer_Test_CB", context) Initialise a timer from MyTimer Lua table. This table should contend 3 elements: + MyTimer={[l"abel"]="MyTimerName", ["delay"]=timeoutInMs, ["count"]=nBOfCycles}. Note that is count==0 then timer is cycle + infinitively. Context is a standard Lua table. This function return an opaque handle to be use to further control the timer. + + * AFB:timerclear(timerHandle) Kill an existing timer. Return an error when timer does not exit. + + * MyTimer=AFB:timerget(timerHandle) Return Label, Delay and Count of an active timer. Return an error when timerHandle does not + point on an active timer. + +Note: Except for function call during binding initialisation period. Lua call are protected and should return clean message + even when improperly used. If you find bug please report. + +### Adding Lua command from User Plugin + +User Plugin is optional and may provide either native C-action accessible directly from controller actions as defined in +JSON config file, or alternatively may provide at set of Lua commands usable inside any script (onload, control,event). A simple +plugin that provide both natice C API and Lua commands is provided as example (see ctl-plugin-sample.c). Technically a +plugin is a simple sharelibrary and any code fitting in sharelib might be used as a plugin. Developer should nevertheless +not forget that except when no-concurrency flag was at binding construction time, any binding should to be thread safe. + +A plugin must be declare with CTLP_REGISTER("MyCtlSamplePlugin"). This entry point defines a special structure that is check +at plugin load time by the controller. Then you have an optional init routine declare with CTLP_ONLOAD(label, version, info). +This init routine receives controller onload profile as selected by DispatchOnLoad("profile"). The init routine may create +a plugin context that is later one presented to every plugin API this for both LUA and native C ones. Then each: + + * C API declare with CTLP_CAPI (MyCFunction, label, argsJ, queryJ, context) {your code}. Where: + * MyFunction is your function + * Label is a string containing the name of your function + * ArgsJ a json_object containing the argument attach the this control in JSON config file. + * context your C context as return from CTLP_ONLOAD + + * Lua API declarewith TLP_LUA2C (MyLuaCFunction, label, argsJ, context) {your code}. Where + * MyLuaCFunction is both the name of your C function and Lua command + * Label your function name as a string + * Args the arguments passed this time from Lua script and not from Json config file. + * Query is not provided as LuaC function are called from a script and not directly from controller action list. + +Warning: Lua samples use with controller enforce strict mode. As a result every variables should be declare either as +local or as global. Unfortunately "luac" is not smart enough to handle strict mode at build time and errors only appear +at run time. Because of this strict mode every global variables (which include functions) should be prefix by '_'. +Note that LUA require an initialisation value for every variables and declaring something like "local myvar" wont +allocate "myvar" + +### Debugging Facilities + +Controler Lua script are check for syntax from CMAKE template with Luac. When needed to go further an developer API allow to +execute directly Lua command within controller context from Rest/Ws (api=control, verb=lua_doscript). DoScript API takes two +other optional arguments func=xxxx where xxxx is the function to execute within Lua script and args a JSON object to provide +input parameter. When funcname is not given by default the controller try to execute middle filename doscript-xxxx-????.lua. + +When executed from controller Lua script may use any AppFw Apis as well as any L2C user defined commands in plugin. + +## Config Sample + +Here after a simple configuration sample. + +``` +{ + "$schema": "ToBeDone", + "metadata": { + "label": "sample-audio-control", + "info": "Provide Default Audio Policy for Multimedia, Navigation and Emergency", + "version": "1.0" + }, + "onload": [{ + "label": "onload-default", + "info": "onload initialisation config", + "plugin": { + "label" : "MyPlug", + "sharelib": "ctl-audio-plugin-sample.ctlso", + "lua2c": ["Lua2cHelloWorld1", "Lua2cHelloWorld2"] + }, + "require": ["intel-hda", "jabra-usb", "scarlett-usb"], + "actions": [ + { + "label": "onload-sample-cb", + "info": "Call control sharelib install entrypoint", + "callback": "SamplePolicyInit", + "args": { + "arg1": "first_arg", + "nextarg": "second arg value" + } + }, { + "label": "onload-sample-api", + "info": "Assert AlsaCore Presence", + "api": "alsacore", + "verb": "ping", + "args": "test" + }, { + "label": "onload-hal-lua", + "info": "Load avaliable HALs", + "lua": "Audio_Init_Hal" + } + ] + }], + "controls": + [ + { + "label": "multimedia", + "permissions": "urn:AGL:permission:audio:public:mutimedia", + "actions": { + "label": "multimedia-control-lua", + "info": "Call Lua Script function Test_Lua_Engin", + "lua": "Audio_Set_Multimedia" + } + }, { + "label": "navigation", + "permissions": "urn:AGL:permission:audio:public:navigation", + "actions": { + "label": "navigation-control-lua", + "info": "Call Lua Script to set Navigation", + "lua": "Audio_Set_Navigation" + } + }, { + "label": "emergency", + "permissions": "urn:AGL:permission:audio:public:emergency", + "actions": { + "label": "emergency-control-ucm", + "lua": "Audio_Set_Emergency" + } + }, { + "label": "multi-step-sample", + "info" : "all actions must succeed for control to be accepted", + "actions": [{ + "label": "multimedia-control-cb", + "info": "Call Sharelib Sample Callback", + "callback": "sampleControlNavigation", + "args": { + "arg1": "snoopy", + "arg2": "toto" + } + }, { + "label": "navigation-control-ucm", + "api": "alsacore", + "verb": "ping", + "args": { + "test": "navigation" + } + }, { + "label": "navigation-control-lua", + "info": "Call Lua Script to set Navigation", + "lua": "Audio_Set_Navigation" + }] + } + ], + "events": + [ + { + "label": "Vehicle-Speed", + "info": "Action when Vehicule speed change", + "actions": [ + { + "label": "speed-action-1", + "callback": "Blink-when-over-130", + "args": { + "speed": 130 + "blink-speed": 1000 + } + }, { + "label": "Adjust-Volume", + "lua": "Adjust_Volume_To_Speed", + } + ] + }, + { + "label": "Reverse-Engage", + "info": "When Reverse Gear is Engage", + "actions": [ + { + "label": "Display-Rear-Camera", + "callback": "Display-Rear-Camera", + }, { + "label": "Prevent-Phone-Call", + "api" : "phone", + "verb" : "status", + "args": { + "call-accepted": false + } + } + ] + }, + { + "label": "Neutral-Engage", + "info": "When Reverse Neutral is Engage", + "actions": [ + { + "label": "Authorize-Video", + "api" : "video", + "verb" : "status", + "args": { + "tv-accepted": true + } + } + ] + } + ] +} + +``` + diff --git a/Controller-afb/a.out b/Controller-afb/a.out new file mode 100755 index 0000000..d18064f Binary files /dev/null and b/Controller-afb/a.out differ diff --git a/Controller-afb/ctl-apidef.h b/Controller-afb/ctl-apidef.h new file mode 100644 index 0000000..68f9725 --- /dev/null +++ b/Controller-afb/ctl-apidef.h @@ -0,0 +1,115 @@ + +static const char _afb_description_v2_control[] = + "{\"openapi\":\"3.0.0\",\"$schema\":\"http:iot.bzh/download/openapi/schem" + "a-3.0/default-schema.json\",\"info\":{\"description\":\"\",\"title\":\"c" + "ontroler\",\"version\":\"1.0\",\"x-binding-c-generator\":{\"api\":\"cont" + "rol\",\"version\":2,\"prefix\":\"ctlapi_\",\"postfix\":\"\",\"start\":nu" + "ll,\"onevent\":\"DispatchOneEvent\",\"init\":\"CtlBindingInit\",\"scope\"" + ":\"\",\"private\":false}},\"servers\":[{\"url\":\"ws://{host}:{port}/api" + "/polctl\",\"description\":\"Unicens2 API.\",\"variables\":{\"host\":{\"d" + "efault\":\"localhost\"},\"port\":{\"default\":\"1234\"}},\"x-afb-events\"" + ":[{\"$ref\":\"#/components/schemas/afb-event\"}]}],\"components\":{\"sch" + "emas\":{\"afb-reply\":{\"$ref\":\"#/components/schemas/afb-reply-v2\"},\"" + "afb-event\":{\"$ref\":\"#/components/schemas/afb-event-v2\"},\"afb-reply" + "-v2\":{\"title\":\"Generic response.\",\"type\":\"object\",\"required\":" + "[\"jtype\",\"request\"],\"properties\":{\"jtype\":{\"type\":\"string\",\"" + "const\":\"afb-reply\"},\"request\":{\"type\":\"object\",\"required\":[\"" + "status\"],\"properties\":{\"status\":{\"type\":\"string\"},\"info\":{\"t" + "ype\":\"string\"},\"token\":{\"type\":\"string\"},\"uuid\":{\"type\":\"s" + "tring\"},\"reqid\":{\"type\":\"string\"}}},\"response\":{\"type\":\"obje" + "ct\"}}},\"afb-event-v2\":{\"type\":\"object\",\"required\":[\"jtype\",\"" + "event\"],\"properties\":{\"jtype\":{\"type\":\"string\",\"const\":\"afb-" + "event\"},\"event\":{\"type\":\"string\"},\"data\":{\"type\":\"object\"}}" + "}},\"x-permissions\":{\"monitor\":{\"permission\":\"urn:AGL:permission:a" + "udio:public:monitor\"},\"multimedia\":{\"permission\":\"urn:AGL:permissi" + "on:audio:public:monitor\"},\"navigation\":{\"permission\":\"urn:AGL:perm" + "ission:audio:public:monitor\"},\"emergency\":{\"permission\":\"urn:AGL:p" + "ermission:audio:public:emergency\"}},\"responses\":{\"200\":{\"descripti" + "on\":\"A complex object array response\",\"content\":{\"application/json" + "\":{\"schema\":{\"$ref\":\"#/components/schemas/afb-reply\"}}}}}},\"path" + "s\":{\"/monitor\":{\"description\":\"Subcribe Audio Agent Policy Control" + " End\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions" + "/monitor\"},\"parameters\":[{\"in\":\"query\",\"name\":\"event_patern\"," + "\"required\":true,\"schema\":{\"type\":\"string\"}}],\"responses\":{\"20" + "0\":{\"$ref\":\"#/components/responses/200\"}}}},\"/dispatch\":{\"descri" + "ption\":\"Request Access to Navigation Audio Channel.\",\"get\":{\"x-per" + "missions\":{\"$ref\":\"#/components/x-permissions/navigation\"},\"parame" + "ters\":[{\"in\":\"query\",\"name\":\"zone\",\"required\":false,\"schema\"" + ":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components" + "/responses/200\"}}}},\"/lua_docall\":{\"description\":\"Execute LUA stri" + "ng script.\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permi" + "ssions/navigation\"},\"parameters\":[{\"in\":\"query\",\"name\":\"func\"" + ",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"" + "name\":\"args\",\"required\":false,\"schema\":{\"type\":\"array\"}}],\"r" + "esponses\":{\"200\":{\"$ref\":\"#/components/responses/200\"}}}},\"/lua_" + "dostring\":{\"description\":\"Execute LUA string script.\",\"get\":{\"x-" + "permissions\":{\"$ref\":\"#/components/x-permissions/navigation\"},\"par" + "ameters\":[{\"in\":\"query\",\"required\":true,\"schema\":{\"type\":\"st" + "ring\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"" + "}}}},\"/lua_doscript\":{\"description\":\"Execute LUA string script.\",\"" + "get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions/navigati" + "on\"},\"parameters\":[{\"in\":\"query\",\"name\":\"filename\",\"required" + "\":true,\"schema\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$re" + "f\":\"#/components/responses/200\"}}}}}}" +; + +static const struct afb_auth _afb_auths_v2_control[] = { + { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:monitor" } +}; + + void ctlapi_monitor(struct afb_req req); + void ctlapi_dispatch(struct afb_req req); + void ctlapi_lua_docall(struct afb_req req); + void ctlapi_lua_dostring(struct afb_req req); + void ctlapi_lua_doscript(struct afb_req req); + +static const struct afb_verb_v2 _afb_verbs_v2_control[] = { + { + .verb = "monitor", + .callback = ctlapi_monitor, + .auth = &_afb_auths_v2_control[0], + .info = NULL, + .session = AFB_SESSION_NONE_V2 + }, + { + .verb = "dispatch", + .callback = ctlapi_dispatch, + .auth = &_afb_auths_v2_control[0], + .info = NULL, + .session = AFB_SESSION_NONE_V2 + }, + { + .verb = "lua_docall", + .callback = ctlapi_lua_docall, + .auth = &_afb_auths_v2_control[0], + .info = NULL, + .session = AFB_SESSION_NONE_V2 + }, + { + .verb = "lua_dostring", + .callback = ctlapi_lua_dostring, + .auth = &_afb_auths_v2_control[0], + .info = NULL, + .session = AFB_SESSION_NONE_V2 + }, + { + .verb = "lua_doscript", + .callback = ctlapi_lua_doscript, + .auth = &_afb_auths_v2_control[0], + .info = NULL, + .session = AFB_SESSION_NONE_V2 + }, + { .verb = NULL } +}; + +const struct afb_binding_v2 afbBindingV2 = { + .api = "control", + .specification = _afb_description_v2_control, + .info = NULL, + .verbs = _afb_verbs_v2_control, + .preinit = NULL, + .init = CtlBindingInit, + .onevent = DispatchOneEvent, + .noconcurrency = 0 +}; + diff --git a/Controller-afb/ctl-apidef.json b/Controller-afb/ctl-apidef.json new file mode 100644 index 0000000..c35cbc0 --- /dev/null +++ b/Controller-afb/ctl-apidef.json @@ -0,0 +1,250 @@ +{ + "openapi": "3.0.0", + "$schema": "http:iot.bzh/download/openapi/schema-3.0/default-schema.json", + "info": { + "description": "", + "title": "controler", + "version": "1.0", + "x-binding-c-generator": { + "api": "control", + "version": 2, + "prefix": "ctlapi_", + "postfix": "", + "start": null, + "onevent": "DispatchOneEvent", + "init": "CtlBindingInit", + "scope": "", + "private": false + } + }, + "servers": [ + { + "url": "ws://{host}:{port}/api/polctl", + "description": "Unicens2 API.", + "variables": { + "host": { + "default": "localhost" + }, + "port": { + "default": "1234" + } + }, + "x-afb-events": [ + { + "$ref": "#/components/schemas/afb-event" + } + ] + } + ], + "components": { + "schemas": { + "afb-reply": { + "$ref": "#/components/schemas/afb-reply-v2" + }, + "afb-event": { + "$ref": "#/components/schemas/afb-event-v2" + }, + "afb-reply-v2": { + "title": "Generic response.", + "type": "object", + "required": ["jtype", "request"], + "properties": { + "jtype": { + "type": "string", + "const": "afb-reply" + }, + "request": { + "type": "object", + "required": ["status"], + "properties": { + "status": { + "type": "string" + }, + "info": { + "type": "string" + }, + "token": { + "type": "string" + }, + "uuid": { + "type": "string" + }, + "reqid": { + "type": "string" + } + } + }, + "response": { + "type": "object" + } + } + }, + "afb-event-v2": { + "type": "object", + "required": ["jtype", "event"], + "properties": { + "jtype": { + "type": "string", + "const": "afb-event" + }, + "event": { + "type": "string" + }, + "data": { + "type": "object" + } + } + } + }, + "x-permissions": { + "monitor": { + "permission": "urn:AGL:permission:audio:public:monitor" + }, + "multimedia": { + "permission": "urn:AGL:permission:audio:public:monitor" + }, + "navigation": { + "permission": "urn:AGL:permission:audio:public:monitor" + }, + "emergency": { + "permission": "urn:AGL:permission:audio:public:emergency" + } + }, + "responses": { + "200": { + "description": "A complex object array response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/afb-reply" + } + } + } + } + } + }, + "paths": { + "/monitor": { + "description": "Subcribe Audio Agent Policy Control End", + "get": { + "x-permissions": { + "$ref": "#/components/x-permissions/monitor" + }, + "parameters": [ + { + "in": "query", + "name": "event_patern", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/200" + } + } + } + }, + "/dispatch": { + "description": "Request Access to Navigation Audio Channel.", + "get": { + "x-permissions": { + "$ref": "#/components/x-permissions/navigation" + }, + "parameters": [ + { + "in": "query", + "name": "zone", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/200" + } + } + } + }, + "/lua_docall": { + "description": "Execute LUA string script.", + "get": { + "x-permissions": { + "$ref": "#/components/x-permissions/navigation" + }, + "parameters": [ + { + "in": "query", + "name": "func", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "args", + "required": false, + "schema": { + "type": "array" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/200" + } + } + } + }, + "/lua_dostring": { + "description": "Execute LUA string script.", + "get": { + "x-permissions": { + "$ref": "#/components/x-permissions/navigation" + }, + "parameters": [ + { + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/200" + } + } + } + }, + "/lua_doscript": { + "description": "Execute LUA string script.", + "get": { + "x-permissions": { + "$ref": "#/components/x-permissions/navigation" + }, + "parameters": [ + { + "in": "query", + "name": "filename", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/200" + } + } + } + } + } +} diff --git a/Controller-afb/ctl-binding.c b/Controller-afb/ctl-binding.c new file mode 100644 index 0000000..8771fc0 --- /dev/null +++ b/Controller-afb/ctl-binding.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author Fulup Ar Foll + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE +#include +#include +#include + +#include "audio-common.h" +#include "ctl-binding.h" + + + +// Include Binding Stub generated from Json OpenAPI +#include "ctl-apidef.h" + + +PUBLIC void ctlapi_monitor (afb_req request) { + + // subscribe Client to event + int err = afb_req_subscribe(request, TimerEvtGet()); + if (err != 0) { + afb_req_fail_f(request, "register-event", "Fail to subscribe binder event"); + goto OnErrorExit; + } + + afb_req_success(request, NULL, NULL); + + OnErrorExit: + return; +} + +// Create Binding Event at Init +PUBLIC int CtlBindingInit () { + + int errcount=0; + + errcount += TimerEvtInit(); + errcount += DispatchInit(); +#ifdef CONTROL_SUPPORT_LUA + errcount += LuaLibInit(); +#endif + + // now that everything is initialised execute the onload action + if (!errcount) + errcount += DispatchOnLoad(CONTROL_ONLOAD_DEFAULT); + + AFB_DEBUG ("Audio Policy Control Binding Done errcount=%d", errcount); + return errcount; +} + diff --git a/Controller-afb/ctl-binding.h b/Controller-afb/ctl-binding.h new file mode 100644 index 0000000..a1c7ef2 --- /dev/null +++ b/Controller-afb/ctl-binding.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author Fulup Ar Foll + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef CONTROLLER_BINDING_INCLUDE +#define CONTROLLER_BINDING_INCLUDE + +#define AFB_BINDING_VERSION 2 +#include +#include +#include +#include +#include + + +#ifdef CONTROL_SUPPORT_LUA +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" +#endif + +#ifndef PUBLIC + #define PUBLIC +#endif +#define STATIC static + +#ifndef UNUSED_ARG +#define UNUSED_ARG(x) UNUSED_ ## x __attribute__((__unused__)) +#define UNUSED_FUNCTION(x) __attribute__((__unused__)) UNUSED_ ## x +#endif + +// polctl-binding.c +PUBLIC int CtlBindingInit (); + +// ctl-timerevt.c +// ---------------------- +typedef int (*timerCallbackT)(void *context); + +typedef struct TimerHandleS { + int count; + int delay; + const char*label; + void *context; + timerCallbackT callback; + sd_event_source *evtSource; +} TimerHandleT; + +PUBLIC int TimerEvtInit (void); +PUBLIC afb_event TimerEvtGet(void); +PUBLIC void TimerEvtStart(TimerHandleT *timerHandle, timerCallbackT callback, void *context); +PUBLIC void TimerEvtStop(TimerHandleT *timerHandle); + +// ctl-policy +// ----------- + +typedef enum { + CTL_MODE_NONE=0, + CTL_MODE_API, + CTL_MODE_CB, + CTL_MODE_LUA, +} CtlRequestModeT; + + +typedef enum { + CTL_SOURCE_CLOSE=-1, + CTL_SOURCE_UNKNOWN=0, + CTL_SOURCE_ONLOAD=1, + CTL_SOURCE_OPEN=2, + CTL_SOURCE_EVENT=3, +} DispatchSourceT; + +typedef struct DispatchActionS{ + const char *info; + const char* label; + CtlRequestModeT mode; + const char* api; + const char* call; + json_object *argsJ; + int timeout; + int (*actionCB)(DispatchSourceT source, const char*label, json_object *argsJ, json_object *queryJ, void *context); +} DispatchActionT; + +typedef int (*Lua2cFunctionT)(char *funcname, json_object *argsJ, void*context); + +PUBLIC int DispatchInit(void); +PUBLIC int DispatchOnLoad(const char *onLoadLabel); +PUBLIC void DispatchOneEvent(const char *evtLabel, json_object *eventJ); +PUBLIC int DispatchOneL2c(lua_State* luaState, char *funcname, Lua2cFunctionT callback); +PUBLIC void ctlapi_dispatch (afb_req request); + +#ifdef CONTROL_SUPPORT_LUA +// ctl-lua.c +typedef int (*Lua2cWrapperT) (lua_State* luaState, char *funcname, Lua2cFunctionT callback); + +#define CTLP_LUA2C(FuncName, label,argsJ, context) static int FuncName(char*label,json_object*argsJ, void*context);\ + int lua2c_ ## FuncName(lua_State* luaState){return((*Lua2cWrap)(luaState, MACRO_STR_VALUE(FuncName), FuncName));};\ + static int FuncName(char* label, json_object* argsJ, void* context) + +typedef enum { + LUA_DOCALL, + LUA_DOSTRING, + LUA_DOSCRIPT, +} LuaDoActionT; + +PUBLIC int LuaLibInit (); +PUBLIC void LuaL2cNewLib(const char *label, luaL_Reg *l2cFunc, int count); +PUBLIC int Lua2cWrapper(lua_State* luaState, char *funcname, Lua2cFunctionT callback, void *context); +PUBLIC int LuaCallFunc (DispatchSourceT source, DispatchActionT *action, json_object *queryJ) ; +PUBLIC void ctlapi_lua_docall (afb_req request); +PUBLIC void ctlapi_lua_dostring (afb_req request); +PUBLIC void ctlapi_lua_doscript (afb_req request); + +#else + typedef void* Lua2cWrapperT; +#endif // CONTROL_SUPPORT_LUA + + +// sharelib ctl-plugin* +typedef struct { + long magic; + char *label; +} CtlPluginMagicT; + + +#define MACRO_STR_VALUE(arg) #arg +#define CTLP_REGISTER(pluglabel) CtlPluginMagicT CtlPluginMagic={.magic=CTL_PLUGIN_MAGIC,.label=pluglabel}; struct afb_binding_data_v2; Lua2cWrapperT Lua2cWrap; +#define CTLP_ONLOAD(label,version,info) void* CtlPluginOnload(char* label, char* version, char* info) +#define CTLP_CAPI(funcname,source, label,argsJ, queryJ, context) int funcname(DispatchSourceT source, char* label, json_object* argsJ, json_object* queryJ, void* context) + + + +#endif // CONTROLLER_BINDING_INCLUDE diff --git a/Controller-afb/ctl-dispatch.c b/Controller-afb/ctl-dispatch.c new file mode 100644 index 0000000..1708204 --- /dev/null +++ b/Controller-afb/ctl-dispatch.c @@ -0,0 +1,665 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author Fulup Ar Foll + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, something express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Reference: + * Json load using json_unpack https://jansson.readthedocs.io/en/2.9/apiref.html#parsing-and-validating-values + */ + +#define _GNU_SOURCE +#include +#include +#include + +#include "ctl-binding.h" + +typedef void*(*DispatchPluginInstallCbT)(const char* label, const char*version, const char*info); + + +static afb_req NULL_AFBREQ = {}; + + +typedef struct { + const char* label; + const char *info; + DispatchActionT *actions; +} DispatchHandleT; + +typedef struct { + const char *label; + const char *info; + void *context; + char *sharelib; + void *dlHandle; + luaL_Reg *l2cFunc; + int l2cCount; +} DispatchPluginT; + +typedef struct { + const char* label; + const char *info; + const char *version; + DispatchPluginT *plugin; + DispatchHandleT **onloads; + DispatchHandleT **events; + DispatchHandleT **controls; +} DispatchConfigT; + +// global config handle +STATIC DispatchConfigT *configHandle = NULL; + +STATIC int DispatchControlToIndex(DispatchHandleT **controls, const char* controlLabel) { + + for (int idx = 0; controls[idx]; idx++) { + if (!strcasecmp(controlLabel, controls[idx]->label)) return idx; + } + return -1; +} + +STATIC int DispatchOneControl(DispatchSourceT source, DispatchHandleT **controls, const char* controlLabel, json_object *queryJ, afb_req request) { + int err; + + if (!configHandle) { + AFB_ERROR("DISPATCH-CTL-API: (Hoops/Bug!!!) No Config Loaded"); + goto OnErrorExit; + } + + if (!configHandle->controls) { + AFB_ERROR("DISPATCH-CTL-API: No Control Action in Json config label=%s version=%s", configHandle->label, configHandle->version); + goto OnErrorExit; + } + + int index = DispatchControlToIndex(controls, controlLabel); + if (index < 0 || !controls[index]->actions) { + AFB_ERROR("DISPATCH-CTL-API:NotFound/Error label=%s in Json Control Config File", controlLabel); + goto OnErrorExit; + } + + // Fulup (Bug/Feature) in current version is unique to every onload profile + if (configHandle->plugin && configHandle->plugin->l2cCount) { + LuaL2cNewLib (configHandle->plugin->label, configHandle->plugin->l2cFunc, configHandle->plugin->l2cCount); + } + + // loop on action for this control + DispatchActionT *actions = controls[index]->actions; + for (int idx = 0; actions[idx].label; idx++) { + + switch (actions[idx].mode) { + case CTL_MODE_API: + { + json_object *returnJ; + + // if query is empty increment usage count and pass args + if (!queryJ || json_object_get_type(queryJ) != json_type_object) { + json_object_get(actions[idx].argsJ); + queryJ= actions[idx].argsJ; + } else if (actions[idx].argsJ) { + + // Merge queryJ and argsJ before sending request + if (json_object_get_type(actions[idx].argsJ) == json_type_object) { + json_object_object_foreach(actions[idx].argsJ, key, val) { + json_object_object_add(queryJ, key, val); + } + } else { + json_object_object_add(queryJ, "args", actions[idx].argsJ); + } + } + + int err = afb_service_call_sync(actions[idx].api, actions[idx].call, queryJ, &returnJ); + if (err) { + static const char*format = "DispatchOneControl(Api) api=%s verb=%s args=%s"; + if (afb_req_is_valid(request))afb_req_fail_f(request, "DISPATCH-CTL-MODE:API", format, actions[idx].label, actions[idx].api, actions[idx].call); + else AFB_ERROR(format, actions[idx].api, actions[idx].call, actions[idx].label); + goto OnErrorExit; + } + break; + } + +#ifdef CONTROL_SUPPORT_LUA + case CTL_MODE_LUA: + err = LuaCallFunc(source, &actions[idx], queryJ); + if (err) { + static const char*format = "DispatchOneControl(Lua) label=%s func=%s args=%s"; + if (afb_req_is_valid(request)) afb_req_fail_f(request, "DISPATCH-CTL-MODE:Lua", format, actions[idx].label, actions[idx].call, json_object_get_string(actions[idx].argsJ)); + else AFB_ERROR(format, actions[idx].label, actions[idx].call, json_object_get_string(actions[idx].argsJ)); + goto OnErrorExit; + } + break; +#endif + + case CTL_MODE_CB: + err = (*actions[idx].actionCB) (source, actions[idx].label, actions[idx].argsJ, queryJ, configHandle->plugin->context); + if (err) { + static const char*format = "DispatchOneControl(Callback) label%s func=%s args=%s"; + if (afb_req_is_valid(request)) afb_req_fail_f(request, "DISPATCH-CTL-MODE:Cb", format, actions[idx].label, actions[idx].call, json_object_get_string(actions[idx].argsJ)); + else AFB_ERROR(format, actions[idx].label, actions[idx].call, json_object_get_string(actions[idx].argsJ)); + goto OnErrorExit; + } + break; + + default: + { + static const char*format = "DispatchOneControl(unknown) mode control=%s action=%s"; + AFB_ERROR(format, controls[index]->label); + if (afb_req_is_valid(request))afb_req_fail_f(request, "DISPATCH-CTL-MODE:Unknown", format, controls[index]->label); + } + } + } + + // everything when fine + if (afb_req_is_valid(request))afb_req_success(request, NULL, controls[index]->label); + return 0; + +OnErrorExit: + return -1; +} + + +// Event name is mapped on control label and executed as a standard control + +PUBLIC void DispatchOneEvent(const char *evtLabel, json_object *eventJ) { + DispatchHandleT **events = configHandle->events; + + (void) DispatchOneControl(CTL_SOURCE_EVENT, events, evtLabel, eventJ, NULL_AFBREQ); +} + +// Event name is mapped on control label and executed as a standard control + +PUBLIC int DispatchOnLoad(const char *onLoadLabel) { + DispatchHandleT **onloads = configHandle->onloads; + + int err = DispatchOneControl(CTL_SOURCE_ONLOAD, onloads, onLoadLabel, NULL, NULL_AFBREQ); + return err; +} + +PUBLIC void ctlapi_dispatch(afb_req request) { + DispatchHandleT **controls = configHandle->controls; + json_object *queryJ, *argsJ=NULL; + const char *target; + DispatchSourceT source= CTL_SOURCE_UNKNOWN; + + queryJ = afb_req_json(request); + int err = wrap_json_unpack(queryJ, "{s:s, s?i s?o !}", "target", &target, "source", &source, "args", &argsJ); + if (err) { + afb_req_fail_f(request, "CTL-DISPTACH-INVALID", "missing target or args not a valid json object query=%s", json_object_get_string(queryJ)); + goto OnErrorExit; + } + + (void) DispatchOneControl(source, controls, target, argsJ, request); + +OnErrorExit: + return; +} + +// Wrapper to Lua2c plugin command add context dans delegate to LuaWrapper +PUBLIC int DispatchOneL2c(lua_State* luaState, char *funcname, Lua2cFunctionT callback) { +#ifndef CONTROL_SUPPORT_LUA + AFB_ERROR("DISPATCH-ONE-L2C: LUA support not selected (cf:CONTROL_SUPPORT_LUA) in config.cmake"); + return 1; +#else + int err=Lua2cWrapper(luaState, funcname, callback, configHandle->plugin->context); + return err; +#endif +} + + +// List Avaliable Configuration Files + +PUBLIC void ctlapi_config(struct afb_req request) { + json_object*tmpJ; + char *dirList; + + + json_object* queryJ = afb_req_json(request); + if (queryJ && json_object_object_get_ex(queryJ, "cfgpath", &tmpJ)) { + dirList = strdup(json_object_get_string(tmpJ)); + } else { + + dirList = getenv("CONTROL_CONFIG_PATH"); + if (!dirList) dirList = strdup(CONTROL_CONFIG_PATH); + AFB_NOTICE("CONFIG-MISSING: use default CONTROL_CONFIG_PATH=%s", CONTROL_CONFIG_PATH); + } + + // get list of config file + struct json_object *responseJ = ScanForConfig(dirList, CTL_SCAN_RECURSIVE, "onload", "json"); + + if (json_object_array_length(responseJ) == 0) { + afb_req_fail(request, "CONFIGPATH:EMPTY", "No Config Found in CONTROL_CONFIG_PATH"); + } else { + afb_req_success(request, responseJ, NULL); + } + + return; +} + +// unpack individual action object + +STATIC int DispatchLoadOneAction(DispatchConfigT *controlConfig, json_object *actionJ, DispatchActionT *action) { + char *api = NULL, *verb = NULL, *callback = NULL, *lua = NULL; + int err, modeCount = 0; + + err = wrap_json_unpack(actionJ, "{ss,s?s,s?s,s?s,s?s,s?s,s?o !}" + , "label", &action->label, "info", &action->info, "callback", &callback, "lua", &lua, "api", &api, "verb", &verb, "args", &action->argsJ); + if (err) { + AFB_ERROR("DISPATCH-LOAD-ACTION Missing something label|info|callback|lua|(api+verb)|args in %s", json_object_get_string(actionJ)); + goto OnErrorExit; + } + + if (lua) { + action->mode = CTL_MODE_LUA; + action->call = lua; + modeCount++; + } + + if (api && verb) { + action->mode = CTL_MODE_API; + action->api = api; + action->call = verb; + modeCount++; + } + + if (callback && controlConfig->plugin) { + action->mode = CTL_MODE_CB; + action->call = callback; + modeCount++; + + action->actionCB = dlsym(controlConfig->plugin->dlHandle, callback); + if (!action->actionCB) { + AFB_ERROR("DISPATCH-LOAD-ACTION fail to find calbback=%s in %s", callback, controlConfig->plugin->sharelib); + goto OnErrorExit; + } + } + + // make sure at least one mode is selected + if (modeCount == 0) { + AFB_ERROR("DISPATCH-LOAD-ACTION No Action Selected lua|callback|(api+verb) in %s", json_object_get_string(actionJ)); + goto OnErrorExit; + } + + if (modeCount > 1) { + AFB_ERROR("DISPATCH-LOAD-ACTION:ToMany arguments lua|callback|(api+verb) in %s", json_object_get_string(actionJ)); + goto OnErrorExit; + } + return 0; + +OnErrorExit: + return -1; +}; + +STATIC DispatchActionT *DispatchLoadActions(DispatchConfigT *controlConfig, json_object *actionsJ) { + int err; + DispatchActionT *actions; + + // action array is close with a nullvalue; + if (json_object_get_type(actionsJ) == json_type_array) { + int count = json_object_array_length(actionsJ); + actions = calloc(count + 1, sizeof (DispatchActionT)); + + for (int idx = 0; idx < count; idx++) { + json_object *actionJ = json_object_array_get_idx(actionsJ, idx); + err = DispatchLoadOneAction(controlConfig, actionJ, &actions[idx]); + if (err) goto OnErrorExit; + } + + } else { + actions = calloc(2, sizeof (DispatchActionT)); + err = DispatchLoadOneAction(controlConfig, actionsJ, &actions[0]); + if (err) goto OnErrorExit; + } + + return actions; + +OnErrorExit: + return NULL; + +} + +STATIC DispatchHandleT *DispatchLoadControl(DispatchConfigT *controlConfig, json_object *controlJ) { + json_object *actionsJ, *permissionsJ; + int err; + + DispatchHandleT *dispatchHandle = calloc(1, sizeof (DispatchHandleT)); + err = wrap_json_unpack(controlJ, "{ss,s?s,s?o, so !}", "label", &dispatchHandle->label, "info", &dispatchHandle->info + , "permissions", &permissionsJ, "actions", &actionsJ); + if (err) { + AFB_ERROR("DISPATCH-LOAD-CONFIG:CONTROL Missing something label|[info]|actions in %s", json_object_get_string(controlJ)); + goto OnErrorExit; + } + + dispatchHandle->actions = DispatchLoadActions(controlConfig, actionsJ); + if (!dispatchHandle->actions) { + AFB_ERROR("DISPATCH-LOAD-CONFIG:CONTROL Error when parsing actions %s", dispatchHandle->label); + goto OnErrorExit; + } + return dispatchHandle; + +OnErrorExit: + return NULL; +} + +STATIC DispatchHandleT *DispatchLoadOnload(DispatchConfigT *controlConfig, json_object *onloadJ) { + json_object *actionsJ = NULL, *requireJ = NULL, *pluginJ = NULL; + int err; + + DispatchHandleT *dispatchHandle = calloc(1, sizeof (DispatchHandleT)); + err = wrap_json_unpack(onloadJ, "{ss,s?s, s?o,s?o,s?o !}", + "label", &dispatchHandle->label, "info", &dispatchHandle->info, "plugin", &pluginJ, "require", &requireJ, "actions", &actionsJ); + if (err) { + AFB_ERROR("DISPATCH-LOAD-CONFIG:ONLOAD Missing something label|[info]|[plugin]|[actions] in %s", json_object_get_string(onloadJ)); + goto OnErrorExit; + } + + // best effort to initialise everything before starting + if (requireJ) { + + void DispatchRequireOneApi(json_object * bindindJ) { + const char* requireBinding = json_object_get_string(bindindJ); + err = afb_daemon_require_api(requireBinding, 1); + if (err) { + AFB_WARNING("DISPATCH-LOAD-CONFIG:REQUIRE Fail to get=%s", requireBinding); + } + } + + if (json_object_get_type(requireJ) == json_type_array) { + for (int idx = 0; idx < json_object_array_length(requireJ); idx++) { + DispatchRequireOneApi(json_object_array_get_idx(requireJ, idx)); + } + } else { + DispatchRequireOneApi(requireJ); + } + } + + if (pluginJ) { + json_object *lua2csJ = NULL; + DispatchPluginT *dPlugin= calloc(1, sizeof(DispatchPluginT)); + controlConfig->plugin = dPlugin; + const char*ldSearchPath=NULL; + + err = wrap_json_unpack(pluginJ, "{ss,s?s,s?s,ss,s?o!}", + "label", &dPlugin->label, "info", &dPlugin->info, "ldpath", &ldSearchPath, "sharelib", &dPlugin->sharelib, "lua2c", &lua2csJ); + if (err) { + AFB_ERROR("DISPATCH-LOAD-CONFIG:ONLOAD Plugin missing label|[info]|sharelib|[lua2c] in %s", json_object_get_string(onloadJ)); + goto OnErrorExit; + } + + // if search path not in Json config file, then try default + if (!ldSearchPath) ldSearchPath=CONTROL_PLUGIN_PATH; + + // search for default policy config file + json_object *pluginPathJ = ScanForConfig(ldSearchPath, CTL_SCAN_RECURSIVE, dPlugin->sharelib, NULL); + if (!pluginPathJ || json_object_array_length(pluginPathJ) == 0) { + AFB_ERROR("DISPATCH-LOAD-CONFIG:PLUGIN Missing plugin=%s in path=%s", dPlugin->sharelib, ldSearchPath); + goto OnErrorExit; + } + + char *filename; + char*fullpath; + err = wrap_json_unpack(json_object_array_get_idx(pluginPathJ, 0), "{s:s, s:s !}", "fullpath", &fullpath, "filename", &filename); + if (err) { + AFB_ERROR("DISPATCH-LOAD-CONFIG:PLUGIN HOOPs invalid plugin file path = %s", json_object_get_string(pluginPathJ)); + goto OnErrorExit; + } + + if (json_object_array_length(pluginPathJ) > 1) { + AFB_WARNING("DISPATCH-LOAD-CONFIG:PLUGIN plugin multiple instances in searchpath will use %s/%s", fullpath, filename); + } + + char pluginpath[CONTROL_MAXPATH_LEN]; + strncpy(pluginpath, fullpath, sizeof (pluginpath)); + strncat(pluginpath, "/", sizeof (pluginpath)); + strncat(pluginpath, filename, sizeof (pluginpath)); + dPlugin->dlHandle = dlopen(pluginpath, RTLD_NOW); + if (!dPlugin->dlHandle) { + AFB_ERROR("DISPATCH-LOAD-CONFIG:PLUGIN Fail to load pluginpath=%s err= %s", pluginpath, dlerror()); + goto OnErrorExit; + } + + CtlPluginMagicT *ctlPluginMagic = (CtlPluginMagicT*) dlsym(dPlugin->dlHandle, "CtlPluginMagic"); + if (!ctlPluginMagic || ctlPluginMagic->magic != CTL_PLUGIN_MAGIC) { + AFB_ERROR("DISPATCH-LOAD-CONFIG:Plugin symbol'CtlPluginMagic' missing or != CTL_PLUGIN_MAGIC plugin=%s", pluginpath); + goto OnErrorExit; + } else { + AFB_NOTICE("DISPATCH-LOAD-CONFIG:Plugin %s successfully registered", ctlPluginMagic->label); + } + + // Jose hack to make verbosity visible from sharelib + struct afb_binding_data_v2 *afbHidenData = dlsym(dPlugin->dlHandle, "afbBindingV2data"); + if (afbHidenData) *afbHidenData = afbBindingV2data; + + // Push lua2cWrapper @ into plugin + Lua2cWrapperT *lua2cInPlug = dlsym(dPlugin->dlHandle, "Lua2cWrap"); +#ifndef CONTROL_SUPPORT_LUA + if (lua2cInPlug) *lua2cInPlug = NULL; +#else + // Lua2cWrapper is part of binder and not expose to dynamic link + if (lua2cInPlug) *lua2cInPlug = DispatchOneL2c; + + { + int Lua2cAddOne(luaL_Reg *l2cFunc, const char* l2cName, int index) { + char funcName[CONTROL_MAXPATH_LEN]; + strncpy(funcName, "lua2c_", sizeof(funcName)); + strncat(funcName, l2cName, sizeof(funcName)); + + Lua2cFunctionT l2cFunction= (Lua2cFunctionT)dlsym(dPlugin->dlHandle, funcName); + if (!l2cFunction) { + AFB_ERROR("DISPATCH-LOAD-CONFIG:Plugin symbol'%s' missing err=%s", funcName, dlerror()); + return 1; + } + l2cFunc[index].func=(void*)l2cFunction; + l2cFunc[index].name=strdup(l2cName); + + return 0; + } + + int errCount = 0; + luaL_Reg *l2cFunc=NULL; + int count=0; + + // look on l2c command and push them to LUA + if (json_object_get_type(lua2csJ) == json_type_array) { + int length = json_object_array_length(lua2csJ); + l2cFunc = calloc(length + 1, sizeof (luaL_Reg)); + for (count=0; count < length; count++) { + int err; + const char *l2cName = json_object_get_string(json_object_array_get_idx(lua2csJ, count)); + err = Lua2cAddOne(l2cFunc, l2cName, count); + if (err) errCount++; + } + } else { + l2cFunc = calloc(2, sizeof (luaL_Reg)); + const char *l2cName = json_object_get_string(lua2csJ); + errCount = Lua2cAddOne(l2cFunc, l2cName, 0); + count=1; + } + if (errCount) { + AFB_ERROR("DISPATCH-LOAD-CONFIG:Plugin %d symbols not found in plugin='%s'", errCount, pluginpath); + goto OnErrorExit; + } else { + dPlugin->l2cFunc= l2cFunc; + dPlugin->l2cCount= count; + } + } +#endif + DispatchPluginInstallCbT ctlPluginOnload = dlsym(dPlugin->dlHandle, "CtlPluginOnload"); + if (ctlPluginOnload) { + dPlugin->context = (*ctlPluginOnload) (controlConfig->label, controlConfig->version, controlConfig->info); + } + } + + dispatchHandle->actions = DispatchLoadActions(controlConfig, actionsJ); + if (!dispatchHandle->actions) { + AFB_ERROR("DISPATCH-LOAD-CONFIG:ONLOAD Error when parsing actions %s", dispatchHandle->label); + goto OnErrorExit; + } + return dispatchHandle; + +OnErrorExit: + return NULL; +} + +STATIC DispatchConfigT *DispatchLoadConfig(const char* filepath) { + json_object *controlConfigJ, *ignoreJ; + int err; + + // Load JSON file + controlConfigJ = json_object_from_file(filepath); + if (!controlConfigJ) { + AFB_ERROR("DISPATCH-LOAD-CONFIG:JsonLoad invalid JSON %s ", filepath); + goto OnErrorExit; + } + + AFB_INFO("DISPATCH-LOAD-CONFIG: loading config filepath=%s", filepath); + + json_object *metadataJ = NULL, *onloadsJ = NULL, *controlsJ = NULL, *eventsJ = NULL; + err = wrap_json_unpack(controlConfigJ, "{s?o,so,s?o,s?o,s?o !}", "$schema", &ignoreJ, "metadata", &metadataJ, "onload", &onloadsJ, "controls", &controlsJ, "events", &eventsJ); + if (err) { + AFB_ERROR("DISPATCH-LOAD-CONFIG Missing something metadata|[onload]|[controls]|[events] in %s", json_object_get_string(controlConfigJ)); + goto OnErrorExit; + } + + DispatchConfigT *controlConfig = calloc(1, sizeof (DispatchConfigT)); + if (metadataJ) { + err = wrap_json_unpack(metadataJ, "{ss,s?s,ss !}", "label", &controlConfig->label, "info", &controlConfig->info, "version", &controlConfig->version); + if (err) { + AFB_ERROR("DISPATCH-LOAD-CONFIG:METADATA Missing something label|version|[label] in %s", json_object_get_string(metadataJ)); + goto OnErrorExit; + } + } + + if (onloadsJ) { + DispatchHandleT *dispatchHandle; + + if (json_object_get_type(onloadsJ) != json_type_array) { + controlConfig->onloads = (DispatchHandleT**) calloc(2, sizeof (void*)); + dispatchHandle = DispatchLoadOnload(controlConfig, onloadsJ); + controlConfig->onloads[0] = dispatchHandle; + } else { + int length = json_object_array_length(onloadsJ); + controlConfig->onloads = (DispatchHandleT**) calloc(length + 1, sizeof (void*)); + + for (int jdx = 0; jdx < length; jdx++) { + json_object *onloadJ = json_object_array_get_idx(onloadsJ, jdx); + dispatchHandle = DispatchLoadOnload(controlConfig, onloadJ); + controlConfig->onloads[jdx] = dispatchHandle; + } + } + } + + if (controlsJ) { + DispatchHandleT *dispatchHandle; + + if (json_object_get_type(controlsJ) != json_type_array) { + controlConfig->controls = (DispatchHandleT**) calloc(2, sizeof (void*)); + dispatchHandle = DispatchLoadControl(controlConfig, controlsJ); + controlConfig->controls[0] = dispatchHandle; + } else { + int length = json_object_array_length(controlsJ); + controlConfig->controls = (DispatchHandleT**) calloc(length + 1, sizeof (void*)); + + for (int jdx = 0; jdx < length; jdx++) { + json_object *controlJ = json_object_array_get_idx(controlsJ, jdx); + dispatchHandle = DispatchLoadControl(controlConfig, controlJ); + controlConfig->controls[jdx] = dispatchHandle; + } + } + } + + if (eventsJ) { + DispatchHandleT *dispatchHandle; + + if (json_object_get_type(eventsJ) != json_type_array) { + controlConfig->events = (DispatchHandleT**) calloc(2, sizeof (void*)); + dispatchHandle = DispatchLoadControl(controlConfig, eventsJ); + controlConfig->events[0] = dispatchHandle; + } else { + int length = json_object_array_length(eventsJ); + controlConfig->events = (DispatchHandleT**) calloc(length + 1, sizeof (void*)); + + for (int jdx = 0; jdx < length; jdx++) { + json_object *eventJ = json_object_array_get_idx(eventsJ, jdx); + dispatchHandle = DispatchLoadControl(controlConfig, eventJ); + controlConfig->events[jdx] = dispatchHandle; + } + } + } + + return controlConfig; + +OnErrorExit: + return NULL; +} + + +// Load default config file at init + +PUBLIC int DispatchInit() { + int index, err, luaLoaded = 0; + char controlFile [CONTROL_MAXPATH_LEN]; + + const char *dirList= getenv("CONTROL_CONFIG_PATH"); + if (!dirList) dirList=CONTROL_CONFIG_PATH; + + strncpy(controlFile, CONTROL_CONFIG_PRE "-", CONTROL_MAXPATH_LEN); + strncat(controlFile, GetBinderName(), CONTROL_MAXPATH_LEN); + + // search for default dispatch config file + json_object* responseJ = ScanForConfig(dirList, CTL_SCAN_RECURSIVE, controlFile, "json"); + + // We load 1st file others are just warnings + for (index = 0; index < json_object_array_length(responseJ); index++) { + json_object *entryJ = json_object_array_get_idx(responseJ, index); + + char *filename; + char*fullpath; + err = wrap_json_unpack(entryJ, "{s:s, s:s !}", "fullpath", &fullpath, "filename", &filename); + if (err) { + AFB_ERROR("DISPATCH-INIT HOOPs invalid JSON entry= %s", json_object_get_string(entryJ)); + goto OnErrorExit; + } + + if (index == 0) { + if (strcasestr(filename, controlFile)) { + char filepath[CONTROL_MAXPATH_LEN]; + strncpy(filepath, fullpath, sizeof (filepath)); + strncat(filepath, "/", sizeof (filepath)); + strncat(filepath, filename, sizeof (filepath)); + configHandle = DispatchLoadConfig(filepath); + if (!configHandle) { + AFB_ERROR("DISPATCH-INIT:ERROR Fail loading [%s]", filepath); + goto OnErrorExit; + } + luaLoaded = 1; + break; + } + } else { + AFB_WARNING("DISPATCH-INIT:WARNING Secondary Control Config Ignored %s/%s", fullpath, filename); + } + } + + // no dispatch config found remove control API from binder + if (!luaLoaded) { + AFB_WARNING("DISPATCH-INIT:WARNING Not Found Control dispatch file [%s]", controlFile); + } + + AFB_NOTICE("DISPATCH-INIT:SUCCES: Audio Control Dispatch Init"); + return 0; + +OnErrorExit: + AFB_NOTICE("DISPATCH-INIT:ERROR: Audio Control Dispatch Init"); + return 1; +} + + + diff --git a/Controller-afb/ctl-lua.c b/Controller-afb/ctl-lua.c new file mode 100644 index 0000000..4ef6b65 --- /dev/null +++ b/Controller-afb/ctl-lua.c @@ -0,0 +1,1033 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author Fulup Ar Foll + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ref: + * (manual) https://www.lua.org/manual/5.3/manual.html + * (lua->C) http://www.troubleshooters.com/codecorn/lua/lua_c_calls_lua.htm#_Anatomy_of_a_Lua_Call + * (lua/C Var) http://acamara.es/blog/2012/08/passing-variables-from-lua-5-2-to-c-and-vice-versa/ + * (Lua/C Lib)https://john.nachtimwald.com/2014/07/12/wrapping-a-c-library-in-lua/ + * (Lua/C Table) https://gist.github.com/SONIC3D/10388137 + * (Lua/C Nested table) https://stackoverflow.com/questions/45699144/lua-nested-table-from-lua-to-c + * (Lua/C Wrapper) https://stackoverflow.com/questions/45699950/lua-passing-handle-to-function-created-with-newlib + * + */ + +#define _GNU_SOURCE +#include +#include + + +#include "ctl-binding.h" +#include "wrap-json.h" + +#define LUA_FIST_ARG 2 // when using luaL_newlib calllback receive libtable as 1st arg +#define LUA_MSG_MAX_LENGTH 512 +#define JSON_ERROR (json_object*)-1 +static afb_req NULL_AFBREQ = {}; + + +static lua_State* luaState; + +#define CTX_MAGIC 123456789 +#define CTX_TOKEN "AFB_ctx" + +typedef struct { + char *name; + int count; + afb_event event; +} LuaAfbEvent; +static LuaAfbEvent *luaDefaultEvt; + +typedef struct { + int ctxMagic; + afb_req request; + void *handle; + char *info; +} LuaAfbContextT; + +typedef struct { + const char *callback; + json_object *context; + void *handle; +} LuaCallServiceT; + +typedef enum { + AFB_MSG_INFO, + AFB_MSG_WARNING, + AFB_MSG_NOTICE, + AFB_MSG_DEBUG, + AFB_MSG_ERROR, +} LuaAfbMessageT; + +/* + * Note(Fulup): I fail to use luaL_setmetatable and replaced it with a simple opaque + * handle while waiting for someone smarter than me to find a better solution + * https://stackoverflow.com/questions/45596493/lua-using-lua-newuserdata-from-lua-pcall + */ + +STATIC LuaAfbContextT *LuaCtxCheck (lua_State *luaState, int index) { + LuaAfbContextT *afbContext; + //luaL_checktype(luaState, index, LUA_TUSERDATA); + //afbContext = (LuaAfbContextT *)luaL_checkudata(luaState, index, CTX_TOKEN); + luaL_checktype(luaState, index, LUA_TLIGHTUSERDATA); + afbContext = (LuaAfbContextT *) lua_touserdata(luaState, index); + if (afbContext == NULL && afbContext->ctxMagic != CTX_MAGIC) { + luaL_error(luaState, "Fail to retrieve user data context=%s", CTX_TOKEN); + AFB_ERROR ("afbContextCheck error retrieving afbContext"); + return NULL; + } + return afbContext; +} + +STATIC LuaAfbContextT *LuaCtxPush (lua_State *luaState, afb_req request, void *handle, const char* info) { + // LuaAfbContextT *afbContext = (LuaAfbContextT *)lua_newuserdata(luaState, sizeof(LuaAfbContextT)); + // luaL_setmetatable(luaState, CTX_TOKEN); + LuaAfbContextT *afbContext = (LuaAfbContextT *)calloc(1, sizeof(LuaAfbContextT)); + lua_pushlightuserdata(luaState, afbContext); + if (!afbContext) { + AFB_ERROR ("LuaCtxPush fail to allocate user data context"); + return NULL; + } + afbContext->ctxMagic=CTX_MAGIC; + afbContext->info=strdup(info); + afbContext->request= request; + afbContext->handle= handle; + return afbContext; +} + +STATIC void LuaCtxFree (LuaAfbContextT *afbContext) { + free(afbContext->info); + free(afbContext); +} + +// Push a json structure on the stack as a LUA table +STATIC int LuaPushArgument (json_object *argsJ) { + + //AFB_NOTICE("LuaPushArgument argsJ=%s", json_object_get_string(argsJ)); + + json_type jtype= json_object_get_type(argsJ); + switch (jtype) { + case json_type_object: { + lua_newtable (luaState); + json_object_object_foreach (argsJ, key, val) { + int done = LuaPushArgument (val); + if (done) { + lua_setfield(luaState,-2, key); + } + } + break; + } + case json_type_array: { + int length= json_object_array_length(argsJ); + lua_newtable (luaState); + for (int idx=0; idx < length; idx ++) { + json_object *val=json_object_array_get_idx(argsJ, idx); + LuaPushArgument (val); + lua_seti (luaState,-2, idx); + } + break; + } + case json_type_int: + lua_pushinteger(luaState, json_object_get_int(argsJ)); + break; + case json_type_string: + lua_pushstring(luaState, json_object_get_string(argsJ)); + break; + case json_type_boolean: + lua_pushboolean(luaState, json_object_get_boolean(argsJ)); + break; + case json_type_double: + lua_pushnumber(luaState, json_object_get_double(argsJ)); + break; + case json_type_null: + AFB_WARNING("LuaPushArgument: NULL object type %s", json_object_get_string(argsJ)); + return 0; + break; + + default: + AFB_ERROR("LuaPushArgument: unsupported Json object type %s", json_object_get_string(argsJ)); + return 0; + } + return 1; +} + +STATIC json_object *LuaPopOneArg (lua_State* luaState, int idx); + +STATIC json_object *LuaTableToJson (lua_State* luaState, int index) { + int idx; + #define LUA_KEY_INDEX -2 + #define LUA_VALUE_INDEX -1 + + json_object *tableJ= json_object_new_object(); + const char *key; + char number[3]; + lua_pushnil(luaState); // 1st key + if (index < 0) index--; + for (idx=1; lua_next(luaState, index) != 0; idx++) { + + // uses 'key' (at index -2) and 'value' (at index -1) + if (lua_type(luaState,LUA_KEY_INDEX) == LUA_TSTRING) key= lua_tostring(luaState, LUA_KEY_INDEX); + else { + snprintf(number, sizeof(number),"%d", idx); + key=number; + } + json_object *argJ= LuaPopOneArg(luaState, LUA_VALUE_INDEX); + json_object_object_add(tableJ, key, argJ); + lua_pop(luaState, 1); // removes 'value'; keeps 'key' for next iteration + } + + // Query is empty free empty json object + if (idx == 1) { + json_object_put(tableJ); + return NULL; + } + return tableJ; +} + +STATIC json_object *LuaPopOneArg (lua_State* luaState, int idx) { + json_object *value=NULL; + + int luaType = lua_type(luaState, idx); + switch(luaType) { + case LUA_TNUMBER: { + lua_Number number= lua_tonumber(luaState, idx);; + int nombre = (int)number; // evil trick to determine wether n fits in an integer. (stolen from ltcl.c) + if (number == nombre) { + value= json_object_new_int((int)number); + } else { + value= json_object_new_double(number); + } + break; + } + case LUA_TBOOLEAN: + value= json_object_new_boolean(lua_toboolean(luaState, idx)); + break; + case LUA_TSTRING: + value= json_object_new_string(lua_tostring(luaState, idx)); + break; + case LUA_TTABLE: + value= LuaTableToJson(luaState, idx); + break; + case LUA_TNIL: + value=json_object_new_string("nil") ; + break; + case LUA_TUSERDATA: + value=json_object_new_int64((int64_t)lua_touserdata(luaState, idx)); // store userdata as int !!! + break; + + default: + AFB_NOTICE ("LuaPopOneArg: script returned Unknown/Unsupported idx=%d type:%d/%s", idx, luaType, lua_typename(luaState, luaType)); + value=NULL; + } + + return value; +} + +static json_object *LuaPopArgs (lua_State* luaState, int start) { + json_object *responseJ; + + int stop = lua_gettop(luaState); + if(stop-start <0) return NULL; + + // start at 2 because we are using a function array lib + if (start == stop) { + responseJ=LuaPopOneArg (luaState, start); + } else { + // loop on remaining return arguments + responseJ= json_object_new_array(); + for (int idx=start; idx <= stop; idx++) { + json_object *argJ=LuaPopOneArg (luaState, idx); + if (!argJ) goto OnErrorExit; + json_object_array_add(responseJ, argJ); + } + } + + return responseJ; + + OnErrorExit: + return NULL; +} + + +STATIC int LuaFormatMessage(lua_State* luaState, LuaAfbMessageT action) { + char *message; + + json_object *responseJ= LuaPopArgs(luaState, LUA_FIST_ARG); + + if (!responseJ) { + luaL_error(luaState,"LuaFormatMessage empty message"); + goto OnErrorExit; + } + + // if we have only on argument just return the value. + if (json_object_get_type(responseJ)!=json_type_array || json_object_array_length(responseJ) <2) { + message= (char*)json_object_get_string(responseJ); + goto PrintMessage; + } + + // extract format and push all other parameter on the stack + message = alloca (LUA_MSG_MAX_LENGTH); + const char *format = json_object_get_string(json_object_array_get_idx(responseJ, 0)); + + int arrayIdx=1; + int targetIdx=0; + + for (int idx=0; format[idx] !='\0'; idx++) { + + if (format[idx]=='%' && format[idx] !='\0') { + json_object *slotJ= json_object_array_get_idx(responseJ, arrayIdx); + //if (slotJ) AFB_NOTICE("**** idx=%d slotJ=%s", arrayIdx, json_object_get_string(slotJ)); + + + switch (format[++idx]) { + case 'd': + if (slotJ) targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"%d", json_object_get_int(slotJ)); + else targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"nil"); + arrayIdx++; + break; + case 'f': + if (slotJ) targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"%f", json_object_get_double(slotJ)); + else targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"nil"); + arrayIdx++; + break; + + case'%': + message[targetIdx]='%'; + targetIdx++; + break; + + case 's': + default: + if (slotJ) targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"%s", json_object_get_string(slotJ)); + else targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"nil"); + arrayIdx++; + } + + } else { + if (targetIdx >= LUA_MSG_MAX_LENGTH) { + AFB_WARNING ("LuaFormatMessage: message[%s] owerverflow LUA_MSG_MAX_LENGTH=%d", format, LUA_MSG_MAX_LENGTH); + targetIdx --; // move backward for EOL + break; + } else { + message[targetIdx++] = format[idx]; + } + } + } + message[targetIdx]='\0'; + +PrintMessage: + switch (action) { + case AFB_MSG_WARNING: + AFB_WARNING (message); + break; + case AFB_MSG_NOTICE: + AFB_NOTICE (message); + break; + case AFB_MSG_DEBUG: + AFB_DEBUG (message); + break; + case AFB_MSG_INFO: + AFB_INFO (message); + break; + case AFB_MSG_ERROR: + default: + AFB_ERROR (message); + } + return 0; // nothing return to lua + + OnErrorExit: // on argument to return (the error message) + return 1; +} + +STATIC int LuaPrintInfo(lua_State* luaState) { + int err=LuaFormatMessage (luaState, AFB_MSG_INFO); + return err; +} + +STATIC int LuaPrintError(lua_State* luaState) { + int err=LuaFormatMessage (luaState, AFB_MSG_ERROR); + return err; // no value return +} + +STATIC int LuaPrintWarning(lua_State* luaState) { + int err=LuaFormatMessage (luaState, AFB_MSG_WARNING); + return err; +} + +STATIC int LuaPrintNotice(lua_State* luaState) { + int err=LuaFormatMessage (luaState, AFB_MSG_NOTICE); + return err; +} + +STATIC int LuaPrintDebug(lua_State* luaState) { + int err=LuaFormatMessage (luaState, AFB_MSG_DEBUG); + return err; +} + +STATIC int LuaAfbSuccess(lua_State* luaState) { + LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG); + if (!afbContext) goto OnErrorExit; + + // ignore context argument + json_object *responseJ= LuaPopArgs(luaState, LUA_FIST_ARG+1); + if (responseJ == JSON_ERROR) return 1; + + afb_req_success(afbContext->request, responseJ, NULL); + + LuaCtxFree(afbContext); + return 0; + + OnErrorExit: + lua_error(luaState); + return 1; +} + +STATIC int LuaAfbFail(lua_State* luaState) { + LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG); + if (!afbContext) goto OnErrorExit; + + json_object *responseJ= LuaPopArgs(luaState, LUA_FIST_ARG+1); + if (responseJ == JSON_ERROR) return 1; + + afb_req_fail(afbContext->request, afbContext->info, json_object_get_string(responseJ)); + + LuaCtxFree(afbContext); + return 0; + + OnErrorExit: + lua_error(luaState); + return 1; +} + +STATIC void LuaAfbServiceCB(void *handle, int iserror, struct json_object *responseJ) { + LuaCallServiceT *contextCB= (LuaCallServiceT*)handle; + int count=1; + + lua_getglobal(luaState, contextCB->callback); + + // push error status & response + lua_pushboolean(luaState, iserror); + count+= LuaPushArgument(responseJ); + count+= LuaPushArgument(contextCB->context); + + int err=lua_pcall(luaState, count, LUA_MULTRET, 0); + if (err) { + AFB_ERROR ("LUA-SERICE-CB:FAIL response=%s err=%s", json_object_get_string(responseJ), lua_tostring(luaState,-1) ); + } + + free (contextCB); +} + + +STATIC int LuaAfbService(lua_State* luaState) { + int count = lua_gettop(luaState); + + // note: argument start at 2 because of AFB: table + if (count <5 || !lua_isstring(luaState, 2) || !lua_isstring(luaState, 3) || !lua_istable(luaState, 4) || !lua_isstring(luaState, 5)) { + lua_pushliteral (luaState, "LuaAfbServiceCall-Syntax is AFB:service_call (api, verb, query, callback, handle ...."); + goto OnErrorExit; + } + + // get api/verb+query + const char *api = lua_tostring(luaState,2); + const char *verb= lua_tostring(luaState,3); + json_object *queryJ= LuaTableToJson(luaState, 4); + if (queryJ == JSON_ERROR) return 1; + + LuaCallServiceT *contextCB = calloc (1, sizeof(LuaCallServiceT)); + contextCB->callback= lua_tostring(luaState, 5); + contextCB->context = LuaPopArgs(luaState, 6); + + afb_service_call(api, verb, queryJ, LuaAfbServiceCB, contextCB); + + return 0; // no value return + + OnErrorExit: + lua_error(luaState); + return 1; +} + +STATIC int LuaAfbServiceSync(lua_State* luaState) { + int count = lua_gettop(luaState); + json_object *responseJ; + + // note: argument start at 2 because of AFB: table + if (count <3 || !lua_isstring(luaState, 2) || !lua_isstring(luaState, 3) || !lua_istable(luaState, 4)) { + lua_pushliteral (luaState, "LuaAfbServiceSync-Syntax is error,data=AFB:service_call (api, verb, query)"); + goto OnErrorExit; + } + + // get api/verb+query + const char *api = lua_tostring(luaState,2); + const char *verb= lua_tostring(luaState,3); + json_object *queryJ= LuaTableToJson(luaState, 4); + + int iserror=afb_service_call_sync (api, verb, queryJ, &responseJ); + + // push error status & response + count=1; lua_pushboolean(luaState, iserror); + count+= LuaPushArgument(responseJ); + + return count; // return count values + + OnErrorExit: + lua_error(luaState); + return 1; +} + +STATIC int LuaAfbEventPush(lua_State* luaState) { + LuaAfbEvent *afbevt; + int index; + + // if no private event handle then use default binding event + if (lua_islightuserdata(luaState, LUA_FIST_ARG)) { + afbevt = (LuaAfbEvent*) lua_touserdata(luaState, LUA_FIST_ARG); + index=LUA_FIST_ARG+1; + } else { + index=LUA_FIST_ARG; + afbevt=luaDefaultEvt; + } + + if (!afb_event_is_valid(afbevt->event)) { + lua_pushliteral (luaState, "LuaAfbMakePush-Fail invalid event"); + goto OnErrorExit; + } + + json_object *ctlEventJ= LuaTableToJson(luaState, index); + if (!ctlEventJ) { + lua_pushliteral (luaState, "LuaAfbEventPush-Syntax is AFB:signal ([evtHandle], {lua table})"); + goto OnErrorExit; + } + + int done = afb_event_push(afbevt->event, ctlEventJ); + if (!done) { + lua_pushliteral (luaState, "LuaAfbEventPush-Fail No Subscriber to event"); + AFB_ERROR ("LuaAfbEventPush-Fail name subscriber event=%s count=%d", afbevt->name, afbevt->count); + goto OnErrorExit; + } + afbevt->count++; + return 0; + + OnErrorExit: + lua_error(luaState); + return 1; +} + +STATIC int LuaAfbEventSubscribe(lua_State* luaState) { + LuaAfbEvent *afbevt; + + LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG); + if (!afbContext) { + lua_pushliteral (luaState, "LuaAfbEventSubscribe-Fail Invalid request handle"); + goto OnErrorExit; + } + + // if no private event handle then use default binding event + if (lua_islightuserdata(luaState, LUA_FIST_ARG+1)) { + afbevt = (LuaAfbEvent*) lua_touserdata(luaState, LUA_FIST_ARG+1); + } else { + afbevt=luaDefaultEvt; + } + + if (!afb_event_is_valid(afbevt->event)) { + lua_pushliteral (luaState, "LuaAfbMakePush-Fail invalid event handle"); + goto OnErrorExit; + } + + int err = afb_req_subscribe(afbContext->request, afbevt->event); + if (err) { + lua_pushliteral (luaState, "LuaAfbEventSubscribe-Fail No Subscriber to event"); + AFB_ERROR ("LuaAfbEventPush-Fail name subscriber event=%s count=%d", afbevt->name, afbevt->count); + goto OnErrorExit; + } + afbevt->count++; + return 0; + + OnErrorExit: + lua_error(luaState); + return 1; +} + +STATIC int LuaAfbEventMake(lua_State* luaState) { + int count = lua_gettop(luaState); + LuaAfbEvent *afbevt=calloc(1,sizeof(LuaAfbEvent)); + + if (count != LUA_FIST_ARG || !lua_isstring(luaState, LUA_FIST_ARG)) { + lua_pushliteral (luaState, "LuaAfbEventMake-Syntax is evtHandle= AFB:event ('myEventName')"); + goto OnErrorExit; + } + + // event name should be the only argument + afbevt->name= strdup (lua_tostring(luaState,LUA_FIST_ARG)); + + // create a new binder event + afbevt->event = afb_daemon_make_event(afbevt->name); + if (!afb_event_is_valid(afbevt->event)) { + lua_pushliteral (luaState, "LuaAfbEventMake-Fail to Create Binder event"); + goto OnErrorExit; + } + + // push event handler as a LUA opaque handle + lua_pushlightuserdata(luaState, afbevt); + return 1; + + OnErrorExit: + lua_error(luaState); + return 1; +} + +// Function call from LUA when lua2c plugin L2C is used +PUBLIC int Lua2cWrapper(lua_State* luaState, char *funcname, Lua2cFunctionT callback, void *context) { + + json_object *argsJ= LuaPopArgs(luaState, LUA_FIST_ARG+1); + int response = (*callback) (funcname, argsJ, context); + + // push response to LUA + lua_pushinteger(luaState, response); + return 1; +} + +// Generated some fake event based on watchdog/counter +PUBLIC int LuaCallFunc (DispatchSourceT source, DispatchActionT *action, json_object *queryJ) { + + int err, count; + + json_object* argsJ = action->argsJ; + const char* func = action->call; + + // load function (should exist in CONTROL_PATH_LUA + lua_getglobal(luaState, func); + + // push source on the stack + count=1; + lua_pushinteger(luaState, source); + + // push argsJ on the stack + if (!argsJ) { + lua_pushnil(luaState); + count++; + } else { + count+= LuaPushArgument (argsJ); + } + + // push queryJ on the stack + if (!queryJ) { + lua_pushnil(luaState); + count++; + } else { + count+= LuaPushArgument (queryJ); + } + + // effectively exec LUA script code + err=lua_pcall(luaState, count, 1, 0); + if (err) { + AFB_ERROR("LuaCallFunc Fail calling %s error=%s", func, lua_tostring(luaState,-1)); + goto OnErrorExit; + } + + // return LUA script value + int rc= (int)lua_tointeger(luaState, -1); + return rc; + + OnErrorExit: + return -1; +} + + +// Execute LUA code from received API request +STATIC void LuaDoAction (LuaDoActionT action, afb_req request) { + + int err, count=0; + + json_object* queryJ = afb_req_json(request); + + switch (action) { + + case LUA_DOSTRING: { + const char *script = json_object_get_string(queryJ); + err=luaL_loadstring(luaState, script); + if (err) { + AFB_ERROR ("LUA-DO-COMPILE:FAIL String=%s err=%s", script, lua_tostring(luaState,-1) ); + goto OnErrorExit; + } + // Push AFB client context on the stack + LuaAfbContextT *afbContext= LuaCtxPush(luaState, request,NULL,script); + if (!afbContext) goto OnErrorExit; + + break; + } + + case LUA_DOCALL: { + const char *func; + json_object *argsJ=NULL; + + err= wrap_json_unpack (queryJ, "{s:s, s?o !}", "func", &func, "args", &argsJ); + if (err) { + AFB_ERROR ("LUA-DOCALL-SYNTAX missing func|args query=%s", json_object_get_string(queryJ)); + goto OnErrorExit; + } + + // load function (should exist in CONTROL_PATH_LUA + lua_getglobal(luaState, func); + + // Push AFB client context on the stack + LuaAfbContextT *afbContext= LuaCtxPush(luaState, request, NULL, func); + if (!afbContext) goto OnErrorExit; + + // push query on the stack + if (!argsJ) { + lua_pushnil(luaState); + count++; + } else { + count+= LuaPushArgument (argsJ); + } + + break; + } + + case LUA_DOSCRIPT: { // Fulup need to fix argument passing + const char *script; + char*func=NULL; + char *filename; char*fullpath; + char luaScriptPath[CONTROL_MAXPATH_LEN]; + json_object *argsJ=NULL; + int index; + + // scan luascript search path once + static json_object *luaScriptPathJ =NULL; + + err= wrap_json_unpack (queryJ, "{s:s, s?s s?o !}", "script", &script,"func", &func, "args", &argsJ); + if (err) { + AFB_ERROR ("LUA-DOSCRIPT-SYNTAX:missing script|(args,arg) query=%s", json_object_get_string(queryJ)); + goto OnErrorExit; + } + + // search for filename=script in CONTROL_LUA_PATH + if (!luaScriptPathJ) luaScriptPathJ= ScanForConfig(CONTROL_LUA_PATH , CTL_SCAN_RECURSIVE,CONTROL_DOSCRIPT_PRE "-", script); + for (index=0; index < json_object_array_length(luaScriptPathJ); index++) { + json_object *entryJ=json_object_array_get_idx(luaScriptPathJ, index); + + err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "fullpath", &fullpath,"filename", &filename); + if (err) { + AFB_ERROR ("LUA-DOSCRIPT-SCAN:HOOPs invalid config file path = %s", json_object_get_string(entryJ)); + goto OnErrorExit; + } + + if (index > 0) AFB_WARNING("LUA-DOSCRIPT-SCAN:Ignore second script=%s path=%s", filename, fullpath); + else { + strncpy (luaScriptPath, fullpath, sizeof(luaScriptPath)); + strncat (luaScriptPath, "/", sizeof(luaScriptPath)); + strncat (luaScriptPath, filename, sizeof(luaScriptPath)); + } + } + + err= luaL_loadfile(luaState, luaScriptPath); + if (err) { + AFB_ERROR ("LUA-DOSCRIPT HOOPs Error in LUA loading scripts=%s err=%s", luaScriptPath, lua_tostring(luaState,-1)); + goto OnErrorExit; + } + + // script was loaded we need to parse to make it executable + err=lua_pcall(luaState, 0, 0, 0); + if (err) { + AFB_ERROR ("LUA-DOSCRIPT:FAIL to load %s", luaScriptPath); + goto OnErrorExit; + } + + // if no func name given try to deduct from filename + if (!func) func= (char*)GetMidleName(filename); + if (!func) { + AFB_ERROR ("LUA-DOSCRIPT:FAIL to deduct funcname from %s", filename); + goto OnErrorExit; + } + + // load function (should exist in CONTROL_PATH_LUA + lua_getglobal(luaState, func); + + // Push AFB client context on the stack + LuaAfbContextT *afbContext= LuaCtxPush(luaState, request, NULL, func); + if (!afbContext) goto OnErrorExit; + + // push function arguments + if (!argsJ) { + lua_pushnil(luaState); + count++; + } else { + count+= LuaPushArgument(argsJ); + } + + break; + } + + default: + AFB_ERROR ("LUA-DOSCRIPT-ACTION unknown query=%s", json_object_get_string(queryJ)); + goto OnErrorExit; + } + + // effectively exec LUA code (afb_reply/fail done later from callback) + err=lua_pcall(luaState, count+1, 0, 0); + if (err) { + AFB_ERROR ("LUA-DO-EXEC:FAIL query=%s err=%s", json_object_get_string(queryJ), lua_tostring(luaState,-1)); + goto OnErrorExit; + } + return; + + OnErrorExit: + afb_req_fail(request,"LUA:ERROR", lua_tostring(luaState,-1)); + return; +} + +PUBLIC void ctlapi_lua_dostring (afb_req request) { + LuaDoAction (LUA_DOSTRING, request); +} + +PUBLIC void ctlapi_lua_docall (afb_req request) { + LuaDoAction (LUA_DOCALL, request); +} + +PUBLIC void ctlapi_lua_doscript (afb_req request) { + LuaDoAction (LUA_DOSCRIPT, request); +} + +STATIC int LuaTimerClear (lua_State* luaState) { + + // Get Timer Handle + LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG); + if (!afbContext) goto OnErrorExit; + + // retrieve useful information opaque handle + TimerHandleT *timerHandle = (TimerHandleT*)afbContext->handle; + + AFB_NOTICE ("LuaTimerClear timer=%s", timerHandle->label); + TimerEvtStop(timerHandle); + + return 0; //happy end + +OnErrorExit: + return 1; +} +STATIC int LuaTimerGet (lua_State* luaState) { + + // Get Timer Handle + LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG); + if (!afbContext) goto OnErrorExit; + + // retrieve useful information opaque handle + TimerHandleT *timerHandle = (TimerHandleT*)afbContext->handle; + + // create response as a JSON object + json_object *responseJ= json_object_new_object(); + json_object_object_add(responseJ,"label", json_object_new_string(timerHandle->label)); + json_object_object_add(responseJ,"delay", json_object_new_int(timerHandle->delay)); + json_object_object_add(responseJ,"count", json_object_new_int(timerHandle->count)); + + // return JSON object as Lua table + int count=LuaPushArgument(responseJ); + + // free json object + json_object_put(responseJ); + + return count; // return argument + +OnErrorExit: + return 0; +} + +// Timer Callback + +// Set timer +STATIC int LuaTimerSetCB (void *handle) { + LuaCallServiceT *contextCB =(LuaCallServiceT*) handle; + TimerHandleT *timerHandle = (TimerHandleT*) contextCB->handle; + int count; + + // push timer handle and user context on Lua stack + lua_getglobal(luaState, contextCB->callback); + + // Push timer handle + LuaAfbContextT *afbContext= LuaCtxPush(luaState, NULL_AFBREQ, contextCB->handle, timerHandle->label); + if (!afbContext) goto OnErrorExit; + count=1; + + // Push user Context + count+= LuaPushArgument(contextCB->context); + + int err=lua_pcall(luaState, count, LUA_MULTRET, 0); + if (err) { + AFB_ERROR ("LUA-TIMER-CB:FAIL response=%s err=%s", json_object_get_string(contextCB->context), lua_tostring(luaState,-1)); + goto OnErrorExit; + } + + // get return parameter + if (!lua_isboolean(luaState, -1)) { + return (lua_toboolean(luaState, -1)); + } + + // timer last run free context resource + if (timerHandle->count == 1) { + LuaCtxFree(afbContext); + } + return 0; // By default we are happy + + OnErrorExit: + return 1; // stop timer +} + +STATIC int LuaTimerSet(lua_State* luaState) { + const char *label=NULL, *info=NULL; + int delay=0, count=0; + + json_object *timerJ = LuaPopOneArg(luaState, LUA_FIST_ARG); + const char *callback = lua_tostring(luaState, LUA_FIST_ARG + 1); + json_object *contextJ = LuaPopOneArg(luaState, LUA_FIST_ARG + 2); + + if (lua_gettop(luaState) != LUA_FIST_ARG+2 || !timerJ || !callback || !contextJ) { + lua_pushliteral(luaState, "LuaTimerSet-Syntax timerset (timerT, 'callback', contextT)"); + goto OnErrorExit; + } + + int err = wrap_json_unpack(timerJ, "{ss, s?s si, si !}", "label", &label, "info", &info, "delay", &delay, "count", &count); + if (err) { + lua_pushliteral(luaState, "LuaTimerSet-Syntax timerT={label:xxx delay:ms, count:xx}"); + goto OnErrorExit; + } + + // everything look fine create timer structure + TimerHandleT *timerHandle = malloc (sizeof (TimerHandleT)); + timerHandle->delay=delay; + timerHandle->count=count; + timerHandle->label=label; + + // Allocate handle to store context and callback + LuaCallServiceT *contextCB = calloc (1, sizeof(LuaCallServiceT)); + contextCB->callback= callback; + contextCB->context = contextJ; + contextCB->handle = timerHandle; + + // fire timer + TimerEvtStart (timerHandle, LuaTimerSetCB, contextCB); + + return 0; // Happy No Return Function + +OnErrorExit: + lua_error(luaState); + return 1; // return error code +} + +// Register a new L2c list of LUA user plugin commands +PUBLIC void LuaL2cNewLib(const char *label, luaL_Reg *l2cFunc, int count) { + // luaL_newlib(luaState, l2cFunc); macro does not work with pointer :( + luaL_checkversion(luaState); + lua_createtable(luaState, 0, count+1); + luaL_setfuncs(luaState,l2cFunc,0); + lua_setglobal(luaState, label); +} + +static const luaL_Reg afbFunction[] = { + {"timerclear", LuaTimerClear}, + {"timerget" , LuaTimerGet}, + {"timerset" , LuaTimerSet}, + {"notice" , LuaPrintNotice}, + {"info" , LuaPrintInfo}, + {"warning" , LuaPrintWarning}, + {"debug" , LuaPrintDebug}, + {"error" , LuaPrintError}, + {"servsync" , LuaAfbServiceSync}, + {"service" , LuaAfbService}, + {"success" , LuaAfbSuccess}, + {"fail" , LuaAfbFail}, + {"subscribe" , LuaAfbEventSubscribe}, + {"evtmake" , LuaAfbEventMake}, + {"evtpush" , LuaAfbEventPush}, + + {NULL, NULL} /* sentinel */ +}; + +// Create Binding Event at Init +PUBLIC int LuaLibInit () { + int err, index; + + // search for default policy config file + char fullprefix[CONTROL_MAXPATH_LEN]; + strncpy (fullprefix, CONTROL_CONFIG_PRE "-", sizeof(fullprefix)); + strncat (fullprefix, GetBinderName(), sizeof(fullprefix)); + strncat (fullprefix, "-", sizeof(fullprefix)); + + json_object *luaScriptPathJ = ScanForConfig(CONTROL_LUA_PATH , CTL_SCAN_RECURSIVE, fullprefix, "lua"); + + // open a new LUA interpretor + luaState = luaL_newstate(); + if (!luaState) { + AFB_ERROR ("LUA_INIT: Fail to open lua interpretor"); + goto OnErrorExit; + } + + // load auxiliary libraries + luaL_openlibs(luaState); + + // redirect print to AFB_NOTICE + luaL_newlib(luaState, afbFunction); + lua_setglobal(luaState, "AFB"); + + // create default lua event to send test pause/resume + luaDefaultEvt=calloc(1,sizeof(LuaAfbEvent)); + luaDefaultEvt->name=CONTROL_LUA_EVENT; + luaDefaultEvt->event = afb_daemon_make_event(CONTROL_LUA_EVENT); + if (!afb_event_is_valid(luaDefaultEvt->event)) { + AFB_ERROR ("POLCTL_INIT: Cannot register lua-events=%s ", CONTROL_LUA_EVENT); + goto OnErrorExit;; + } + + // load+exec any file found in LUA search path + for (index=0; index < json_object_array_length(luaScriptPathJ); index++) { + json_object *entryJ=json_object_array_get_idx(luaScriptPathJ, index); + + char *filename; char*fullpath; + err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "fullpath", &fullpath,"filename", &filename); + if (err) { + AFB_ERROR ("LUA-INIT HOOPs invalid config file path = %s", json_object_get_string(entryJ)); + goto OnErrorExit; + } + + char filepath[CONTROL_MAXPATH_LEN]; + strncpy(filepath, fullpath, sizeof(filepath)); + strncat(filepath, "/", sizeof(filepath)); + strncat(filepath, filename, sizeof(filepath)); + err= luaL_loadfile(luaState, filepath); + if (err) { + AFB_ERROR ("LUA-LOAD HOOPs Error in LUA loading scripts=%s err=%s", filepath, lua_tostring(luaState,-1)); + goto OnErrorExit; + } + + // exec/compil script + err = lua_pcall(luaState, 0, 0, 0); + if (err) { + AFB_ERROR ("LUA-LOAD HOOPs Error in LUA exec scripts=%s err=%s", filepath, lua_tostring(luaState,-1)); + goto OnErrorExit; + } + } + + // no policy config found remove control API from binder + if (index == 0) { + AFB_WARNING ("POLICY-INIT:WARNING No Control LUA file in path=[%s]", CONTROL_LUA_PATH); + } + + AFB_DEBUG ("Audio control-LUA Init Done"); + return 0; + + OnErrorExit: + return 1; +} + \ No newline at end of file diff --git a/Controller-afb/ctl-plugin-sample.c b/Controller-afb/ctl-plugin-sample.c new file mode 100644 index 0000000..1d66802 --- /dev/null +++ b/Controller-afb/ctl-plugin-sample.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author Fulup Ar Foll + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Sample plugin for Controller + */ + + +#define _GNU_SOURCE +#include +#include + +#include "ctl-binding.h" + +#define MY_PLUGIN_MAGIC 987654321 + +typedef struct { + int magic; + int count; +} MyPluginCtxT; + +STATIC const char* jsonToString (json_object *valueJ) { + const char *value; + if (valueJ) + value=json_object_get_string(valueJ); + else + value="NULL"; + + return value; +} + +// Declare this sharelib as a Controller Plugin +CTLP_REGISTER("MyCtlSamplePlugin"); + +// Call at initialisation time +PUBLIC CTLP_ONLOAD(label, version, info) { + MyPluginCtxT *pluginCtx= (MyPluginCtxT*)calloc (1, sizeof(MyPluginCtxT)); + pluginCtx->magic = MY_PLUGIN_MAGIC; + pluginCtx->count = -1; + + AFB_NOTICE ("CONTROLLER-PLUGIN-SAMPLE:Onload label=%s version=%s info=%s", label, info, version); + return (void*)pluginCtx; +} + +PUBLIC CTLP_CAPI (SamplePolicyInit, source, label, argsJ, queryJ, context) { + MyPluginCtxT *pluginCtx= (MyPluginCtxT*)context; + if (!context || pluginCtx->magic != MY_PLUGIN_MAGIC) { + AFB_ERROR("CONTROLLER-PLUGIN-SAMPLE:SamplePolicyInit (Hoops) Invalid Sample Plugin Context"); + return -1; + }; + + pluginCtx->count = 0; + AFB_NOTICE ("CONTROLLER-PLUGIN-SAMPLE:Init label=%s args=%s\n", label, jsonToString(argsJ)); + return 0; +} + +PUBLIC CTLP_CAPI (sampleControlMultimedia, source, label, argsJ,queryJ,context) { + MyPluginCtxT *pluginCtx= (MyPluginCtxT*)context; + + if (!context || pluginCtx->magic != MY_PLUGIN_MAGIC) { + AFB_ERROR("CONTROLLER-PLUGIN-SAMPLE:sampleControlMultimedia (Hoops) Invalid Sample Plugin Context"); + return -1; + }; + pluginCtx->count++; + AFB_NOTICE ("CONTROLLER-PLUGIN-SAMPLE:sampleControlMultimedia SamplePolicyCount action=%s args=%s query=%s count=%d" + , label, jsonToString(argsJ), jsonToString(queryJ), pluginCtx->count); + return 0; +} + +PUBLIC CTLP_CAPI (sampleControlNavigation, source, label, argsJ, queryJ, context) { + MyPluginCtxT *pluginCtx= (MyPluginCtxT*)context; + + if (!context || pluginCtx->magic != MY_PLUGIN_MAGIC) { + AFB_ERROR("CONTROLLER-PLUGIN-SAMPLE:sampleControlNavigation (Hoops) Invalid Sample Plugin Context"); + return -1; + }; + pluginCtx->count++; + AFB_NOTICE ("CONTROLLER-PLUGIN-SAMPLE:sampleControlNavigation SamplePolicyCount action=%s args=%s query=%s count=%d" + ,label, jsonToString(argsJ), jsonToString(queryJ), pluginCtx->count); + return 0; +} + +PUBLIC CTLP_CAPI (SampleControlEvent, source, label, argsJ, queryJ, context) { + MyPluginCtxT *pluginCtx= (MyPluginCtxT*)context; + + if (!context || pluginCtx->magic != MY_PLUGIN_MAGIC) { + AFB_ERROR("CONTROLLER-PLUGIN-SAMPLE:cousampleControlMultimediant (Hoops) Invalid Sample Plugin Context"); + return -1; + }; + pluginCtx->count++; + AFB_NOTICE ("CONTROLLER-PLUGIN-SAMPLE:sampleControlMultimedia SamplePolicyCount action=%s args=%s query=%s count=%d" + ,label, jsonToString(argsJ), jsonToString(queryJ), pluginCtx->count); + return 0; +} + +// This function is a LUA function. Lua2CHelloWorld label should be declare in the "onload" section of JSON config file +PUBLIC CTLP_LUA2C (Lua2cHelloWorld1, label, argsJ, context) { + MyPluginCtxT *pluginCtx= (MyPluginCtxT*)context; + + if (!context || pluginCtx->magic != MY_PLUGIN_MAGIC) { + AFB_ERROR("CONTROLLER-PLUGIN-SAMPLE:Lua2cHelloWorld1 (Hoops) Invalid Sample Plugin Context"); + return -1; + }; + pluginCtx->count++; + AFB_NOTICE ("CONTROLLER-PLUGIN-SAMPLE:Lua2cHelloWorld1 SamplePolicyCount action=%s args=%s count=%d" + ,label, jsonToString(argsJ), pluginCtx->count); + return 0; +} + +// This function is a LUA function. Lua2CHelloWorld label should be declare in the "onload" section of JSON config file +PUBLIC CTLP_LUA2C (Lua2cHelloWorld2, label, argsJ, context) { + MyPluginCtxT *pluginCtx= (MyPluginCtxT*)context; + + if (!context || pluginCtx->magic != MY_PLUGIN_MAGIC) { + AFB_ERROR("CONTROLLER-PLUGIN-SAMPLE:Lua2cHelloWorld2 (Hoops) Invalid Sample Plugin Context"); + return -1; + }; + pluginCtx->count++; + AFB_NOTICE ("CONTROLLER-PLUGIN-SAMPLE:Lua2cHelloWorld2 SamplePolicyCount action=%s args=%s count=%d" + ,label, jsonToString(argsJ), pluginCtx->count); + return 0; +} diff --git a/Controller-afb/ctl-policy.c b/Controller-afb/ctl-policy.c new file mode 100644 index 0000000..e9798b3 --- /dev/null +++ b/Controller-afb/ctl-policy.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author Fulup Ar Foll + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, something express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include "wrap-json.h" +#include "ctl-binding.h" + +STATIC PolicyCtlConfigT *ctlHandle = NULL; + +// List Avaliable Configuration Files +STATIC json_object* ScanForConfig (char* searchPath) { + json_object *responseJ; + DIR *dirHandle; + char *dirPath; + char* dirList= strdup(searchPath); + + responseJ = json_object_new_array(); + for (dirPath= strtok(dirList, ":"); dirPath && *dirPath; dirPath=strtok(NULL,":")) { + struct dirent *dirEnt; + + dirHandle = opendir (dirPath); + if (!dirHandle) { + AFB_NOTICE ("CONFIG-SCANNING dir=%s not readable", dirPath); + continue; + } + + AFB_NOTICE ("CONFIG-SCANNING:ctl_listconfig scanning: %s", dirPath); + while ((dirEnt = readdir(dirHandle)) != NULL) { + // Unknown type is accepted to support dump filesystems + if (dirEnt->d_type == DT_REG || dirEnt->d_type == DT_UNKNOWN) { + struct json_object *pathJ = json_object_new_object(); + json_object_object_add(pathJ, "dirpath", json_object_new_string(dirPath)); + json_object_object_add(pathJ, "filename", json_object_new_string(dirEnt->d_name)); + json_object_array_add(responseJ, pathJ); + } + } + } + + free (dirList); + return (responseJ); +} + +PUBLIC void ctlapi_authorize (PolicyCtlEnumT control, afb_req request) { + json_object*tmpJ; + + json_object* queryJ= afb_req_json(request); + int done=json_object_object_get_ex(queryJ, "closing", &tmpJ); + if (done) return; + +} + + +// List Avaliable Configuration Files +PUBLIC void ctlapi_config (struct afb_req request) { + json_object*tmpJ; + char *dirList; + + json_object* queryJ = afb_req_json(request); + if (queryJ && json_object_object_get_ex (queryJ, "cfgpath" , &tmpJ)) { + dirList = strdup (json_object_get_string(tmpJ)); + } else { + dirList = strdup (CONTROL_CONFIG_PATH); + AFB_NOTICE ("CONFIG-MISSING: use default CONTROL_CONFIG_PATH=%s", CONTROL_CONFIG_PATH); + } + + // get list of config file + struct json_object *responseJ = ScanForConfig(dirList); + + if (json_object_array_length(responseJ) == 0) { + afb_req_fail(request, "CONFIGPATH:EMPTY", "No Config Found in CONTROL_CONFIG_PATH"); + } else { + afb_req_success(request, responseJ, NULL); + } + + return; +} + +STATIC PolicyActionT **PolicyLoadActions (json_object *actionsJ) { + int err; + PolicyActionT ** actions; + + // unpack individual action object + int actionUnpack (json_object *actionJ, PolicyActionT *action) { + + err= wrap_json_unpack(actionJ, "{ss,s?s,s?s,s?s,s?s,s?s !}" + , "label",&action->label, "info",&action->info, "callback",&action->callback, "query",&queryJ, "api",&action->api, "verb", &action->verb); + if (err) { + AFB_ERROR ("POLICY-LOAD-ACTION Missing something label|info|callback|api|verb|query in %s", json_object_get_string(actionJ)); + return -1; + } + if (!action->callback || !(action->api && action->verb)) { + AFB_ERROR ("POLICY-LOAD-ACTION Missing something callback|(api+verb) in %s", json_object_get_string(actionJ)); + return -1; + } + return 0; + }; + + // action array is close with a nullvalue; + if (json_object_get_type(actionsJ) == json_type_array) { + int count = json_object_array_length(actionsJ); + actions = calloc (count+1, sizeof(PolicyActionT)); + + for (int idx=0; idx < count; idx++) { + json_object *actionJ = json_object_array_get_idx(actionsJ, idx); + err = actionUnpack (actionJ, &actions[idx]); + if (err) goto OnErrorExit; + } + + } else { + actions = calloc (2, sizeof(PolicyActionT)); + err = actionUnpack (actionsJ, &actions[0]); + if (err) goto OnErrorExit; + } + + return actions; + + OnErrorExit: + return NULL; + +} + +// load control policy from file using json_unpack https://jansson.readthedocs.io/en/2.9/apiref.html#parsing-and-validating-values +STATIC PolicyCtlConfigT *PolicyLoadConfig (const char* filepath) { + json_object *policyConfigJ, *ignoreJ, *actionsJ; + PolicyCtlConfigT *policyConfig = calloc (1, sizeof(PolicyCtlConfigT)); + int err; + + // Load JSON file + policyConfigJ= json_object_from_file(filepath); + if (!policyConfigJ) goto OnErrorExit; + + json_object *metadataJ, *onloadJ, *controlsJ, *eventsJ; + err= wrap_json_unpack(policyConfigJ, "{s?o,so,s?o,so,so !}", "$schema", &ignoreJ, "metadata",&metadataJ, "onload",&onloadJ, "controls",&controlsJ, "events",&eventsJ); + if (err) { + AFB_ERROR ("POLICY-LOAD-ERRROR Missing something metadata|onload|controls|events in %s", json_object_get_string(policyConfigJ)); + goto OnErrorExit; + } + + PolicyHandleT *policyHandle = calloc (1, sizeof(PolicyHandleT)); + err= wrap_json_unpack(metadataJ, "{so,s?s,s?s !}", "label", &policyHandle->label, "info",&policyHandle->info, "version",&policyHandle->version); + if (err) { + AFB_ERROR ("POLICY-LOAD-CONFIG Missing something label|info|version in %s", json_object_get_string(metadataJ)); + goto OnErrorExit; + } + + if (onloadJ) { + err= wrap_json_unpack(onloadJ, "{s?o,s?s,s?s !}", "info",&ignoreJ, "label",&ignoreJ, "actions",&actionsJ); + if (err) { + AFB_ERROR ("POLICY-LOAD-CONFIG Missing something label|info|plugin|actions in %s", json_object_get_string(metadataJ)); + goto OnErrorExit; + } + policyConfig->onload = PolicyLoadActions (actionsJ); + } + + return policyControl; + +OnErrorExit: + return NULL; +} + +// Load default config file at init +PUBLIC int PolicyInit () { + int index, err; + + + // search for default policy config file + json_object* responseJ = ScanForConfig(CONTROL_CONFIG_PATH); + + for (index=0; index < json_object_array_length(responseJ); index++) { + json_object *entryJ=json_object_array_get_idx(responseJ, index); + + char *filename; char*dirpath; + err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "dirpath", &dirpath,"filename", &filename); + if (err) { + AFB_ERROR ("POLICY-INIT HOOPs invalid config file path = %s", json_object_get_string(entryJ)); + goto OnErrorExit; + } + + if (strcasestr(filename, CONTROL_CONFIG_FILE)) { + char filepath[255]; + strncpy(filepath, dirpath, sizeof(filepath)); + strncat(filepath, "/", sizeof(filepath)); + strncat(filepath, filename, sizeof(filepath)); + ctlHandle = PolicyLoadConfig (filepath); + if (!ctlHandle) goto OnErrorExit; + break; + } + } + + // no policy config found remove control API from binder + if (index == 0) goto OnErrorExit; + + AFB_NOTICE ("SUCCES: Audio Control Policy Init"); + return 0; + +OnErrorExit: + AFB_NOTICE ("ERROR: Audio Control Policy Init"); + return 1; +} + + + diff --git a/Controller-afb/ctl-timer.c b/Controller-afb/ctl-timer.c new file mode 100644 index 0000000..a826f9f --- /dev/null +++ b/Controller-afb/ctl-timer.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author Fulup Ar Foll + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include "ctl-binding.h" + +#define DEFAULT_PAUSE_DELAY 3000 +#define DEFAULT_TEST_COUNT 1 +typedef struct { + int value; + const char *label; +} AutoTestCtxT; + +static afb_event afbevt; + +STATIC int TimerNext (sd_event_source* source, uint64_t timer, void* handle) { + TimerHandleT *timerHandle = (TimerHandleT*) handle; + int done; + uint64_t usec; + + // Rearm timer if needed + timerHandle->count --; + if (timerHandle->count == 0) { + sd_event_source_unref(source); + free (handle); + return 0; + } + else { + // otherwise validate timer for a new run + sd_event_now(afb_daemon_get_event_loop(), CLOCK_MONOTONIC, &usec); + sd_event_source_set_enabled(source, SD_EVENT_ONESHOT); + sd_event_source_set_time(source, usec + timerHandle->delay*1000); + } + + done= timerHandle->callback(timerHandle->context); + if (!done) goto OnErrorExit; + + return 0; + +OnErrorExit: + AFB_WARNING("TimerNext Callback Fail Tag=%s", timerHandle->label); + return -1; +} + +PUBLIC void TimerEvtStop(TimerHandleT *timerHandle) { + + sd_event_source_unref(timerHandle->evtSource); + free (timerHandle); +} + + +PUBLIC void TimerEvtStart(TimerHandleT *timerHandle, timerCallbackT callback, void *context) { + uint64_t usec; + + // populate CB handle + timerHandle->callback=callback; + timerHandle->context=context; + + // set a timer with ~250us accuracy + sd_event_now(afb_daemon_get_event_loop(), CLOCK_MONOTONIC, &usec); + sd_event_add_time(afb_daemon_get_event_loop(), &timerHandle->evtSource, CLOCK_MONOTONIC, usec+timerHandle->delay*1000, 250, TimerNext, timerHandle); +} + +PUBLIC afb_event TimerEvtGet(void) { + return afbevt; +} + + +// Create Binding Event at Init +PUBLIC int TimerEvtInit () { + + // create binder event to send test pause/resume + afbevt = afb_daemon_make_event("control"); + if (!afb_event_is_valid(afbevt)) { + AFB_ERROR ("POLCTL_INIT: Cannot register ctl-events"); + return 1; + } + + AFB_DEBUG ("Audio Control-Events Init Done"); + return 0; +} + diff --git a/HAL-afb/HAL-interface/hal-interface.c b/HAL-afb/HAL-interface/hal-interface.c index 4d86504..0abe67e 100644 --- a/HAL-afb/HAL-interface/hal-interface.c +++ b/HAL-afb/HAL-interface/hal-interface.c @@ -44,7 +44,7 @@ STATIC int halCtlStringToIndex(const char* label) { alsaHalMapT *halCtls = halSndCard->ctls; for (int idx = 0; halCtls[idx].tag != EndHalCrlTag; idx++) { - if (halCtls[idx].label && !strcmp(halCtls[idx].label, label)) return idx; + if (halCtls[idx].label && !strcasecmp(halCtls[idx].label, label)) return idx; } // not found @@ -502,11 +502,12 @@ PUBLIC int halServiceInit(const char *apiPrefix, alsaHalSndCardT *alsaHalSndCard json_object *ctlJ; // Try to find best equivalent label for tag - if (halCtls[idx].tag >StartHalCrlTag && halCtls[idx].tag < EndHalCrlTag && halCtls[idx].label != NULL) { + if (halCtls[idx].tag >StartHalCrlTag && halCtls[idx].tag < EndHalCrlTag && halCtlsLabels[halCtls[idx].tag] != NULL) { halCtls[idx].label = halCtlsLabels[halCtls[idx].tag]; } else { if (halCtls[idx].ctl.name) halCtls[idx].label=halCtls[idx].ctl.name; else if (halCtls[idx].info) halCtls[idx].label=halCtls[idx].info; + else halCtls[idx].label="NoHalCttNameSet"; } ctlJ = json_object_new_object(); @@ -617,9 +618,9 @@ PUBLIC void halServiceEvent(const char *evtname, json_object *eventJ) { PUBLIC afb_verb_v2 halServiceApi[] = { /* VERB'S NAME FUNCTION TO CALL SHORT DESCRIPTION */ { .verb = "ping", .callback = pingtest, .info = "ping test for API"}, - { .verb = "ctl-list", .callback = halListCtls, .info = "List AGL normalised Sound Controls"}, - { .verb = "ctl-get", .callback = halGetCtls, .info = "Get one/many sound controls"}, - { .verb = "ctl-set", .callback = halSetCtls, .info = "Set one/many sound controls"}, - { .verb = "evt-sub", .callback = halSubscribe, .info = "Subscribe to HAL events"}, + { .verb = "ctllist", .callback = halListCtls, .info = "List AGL normalised Sound Controls"}, + { .verb = "ctlget", .callback = halGetCtls, .info = "Get one/many sound controls"}, + { .verb = "ctlset", .callback = halSetCtls, .info = "Set one/many sound controls"}, + { .verb = "evtsub", .callback = halSubscribe, .info = "Subscribe to HAL events"}, { .verb = NULL} /* marker for end of the array */ }; diff --git a/conf.d/alsa-sample/asoundrc.sample b/conf.d/alsa-sample/asoundrc.sample deleted file mode 100644 index 1786c46..0000000 --- a/conf.d/alsa-sample/asoundrc.sample +++ /dev/null @@ -1,160 +0,0 @@ -# -# Author: Fulup Ar Foll -# Object: PCM hook type -# -# Test : Note: Jabra_USB=hw:v1340 -# Check SoundCard speaker-test -Dhw:v1340 -c2 -twav -# Check MixerPCM speaker-test -DMyMixerPCM -c2 -twav -# Check HookPCM speaker-test -DMyNavigationHook -c2 -twav -# Check NavPCM speaker-test -DMyNavPCM -c2 -twav -# MultiMedia aplay -DDMyNavPCM /usr/share/sounds/alsa/test.wav -# -# Bug/Feature: when softvol control is initialised from plugin and not -# from AGL binding. At 1st run ctl has invalid TLV and cannot be -# use. Bypass Solution: -# * start audio-binder before playing sound (binding create control before softvol plugin) -# * run a dummy aplay -DMyNavPCM "" to get a clean control -# -# References: https://www.spinics.net/lists/alsa-devel/msg54235.html -# -------------------------------------------------------------------- - -# Mixer PCM allow to play multiple stream simultaneously -# ------------------------------------------------------ -pcm.MyMixerPCM { - type dmix - ipc_key 1024 - ipc_key_add_uid false - ipc_perm 0666 # mixing for all users - - # Define target effective sound card (cannot be a plugin) - slave { - pcm "hw:v1340" #Jabra Solmate - period_time 0 - period_size 1024 - buffer_size 8192 - rate 44100 - } - - # DMIX can only map two channels - bindings { - 0 0 - 1 1 - } -} - -# Define a Hook_type with a private sharelib -# ------------------------------------------- -pcm_hook_type.MyHookPlugin { - install "AlsaInstallHook" - lib "/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build/Alsa-Plugin/Alsa-Policy-Hook/policy_hook_cb.so" -} - -# Define a HookedPCM that point to Hook_type sharelib -# ---------------------------------------------------- -pcm.MyNavigationHook { - type hooks - slave.pcm "MyMixerPCM" - - # Defined used hook sharelib and provide arguments/config to install func - hooks.0 { - type "MyHookPlugin" - hook_args { - verbose true # print few log messages (default false); - - # Every Call should return OK in order PCM to open (default timeout 100ms) - uri "ws://localhost:1234/api?token='audio-agent-token'" - request { - # Request autorisation to write on navigation - RequestNavigation { - api "polctl" - verb "navigation" - } - # subscribe to Audio Agent Event - SubscriveEvents { - api "polctl" - verb "monitor" - } - TestAutoStop { - api "polctl" - verb "event_test" - query "{'label':'stop', 'delay':10000}" - } - TestAutoStart { - api "polctl" - verb "event_test" - query "{'label':'stop', 'delay':20000}" - } - # start a test pause/resume not supported by every hardware - TestPauseResume { - api "polctl" - verb "event_test" - query "{'label':'pause', 'delay':3000, 'count':10}" - } - } - } - } -} - -# If hardware does not support mixer emulate it with softvol -# ----------------------------------------------------------- -pcm.MyMultimediaPCM { - type softvol - - # Point Slave on HOOK for policies control - slave.pcm "MyNavigationHook" - - # resolution=HAL(valMax+1) (default=256) - resolution 256 - - # name should match with HAL but do not set card=xx - control.name "Playback Navigation" - - # Make this plugin visible from aplay -L - hint { - show on - description "Navigation SolftVol PCM" - } -} - -# If hardware does not support mixer emulate it with softvol -# ----------------------------------------------------------- -pcm.MyNavPCM { - type softvol - - # Point Slave on HOOK for policies control - slave.pcm "MyNavigationHook" - - # resolution=HAL(valMax+1) (default=256) - resolution 256 - - # name should match with HAL but do not set card=xx - control.name "Playback Navigation" - - # Make this plugin visible from aplay -L - hint { - show on - description "Navigation SolftVol PCM" - } -} - -# If hardware does not support mixer emulate it with softvol -# ----------------------------------------------------------- -pcm.MyAlarmPCM { - type softvol - - # Point Slave on HOOK for policies control - slave.pcm "MyNavigationHook" - - # resolution=HAL(valMax+1) (default=256) - resolution 256 - - # name should match with HAL but do not set card=xx - control.name "Playback Navigation" - - # Make this plugin visible from aplay -L - hint { - show on - description "Navigation SolftVol PCM" - } -} - diff --git a/conf.d/alsa-sample/ucm.sample/HDA Intel PCH.conf b/conf.d/alsa-sample/ucm.sample/HDA Intel PCH.conf deleted file mode 100644 index f6608a0..0000000 --- a/conf.d/alsa-sample/ucm.sample/HDA Intel PCH.conf +++ /dev/null @@ -1,6 +0,0 @@ -Comment "Leon internal card" - -SectionUseCase."HiFi" { - File "HiFi.conf" - Comment "Default" -} diff --git a/conf.d/alsa-sample/ucm.sample/HiFi.conf b/conf.d/alsa-sample/ucm.sample/HiFi.conf deleted file mode 100644 index 9a53c8c..0000000 --- a/conf.d/alsa-sample/ucm.sample/HiFi.conf +++ /dev/null @@ -1,84 +0,0 @@ -SectionVerb { - EnableSequence [ - cdev "hw:PCH" - - cset "name='Master Playback Switch' on" - cset "name='Headphone Playback Switch' off" - cset "name='Speaker Playback Switch' on" - - cset "name='Capture Switch' on" - cset "name='Capture Volume' 39" - cset "name='Mic Boost Volume' 2" - cset "name='Internal Mic Boost Volume' 0" - #cset "name='Capture Source' 0" - ] - DisableSequence [ - ] - Value { - TQ "Music" - OutputDspName "speaker_eq" - PlaybackPCM "hw:PCH,0" - } -} - -SectionDevice."Headphone".0 { - Value { - JackName "Headphone Jack" - OutputDspName "Jheadphone" - } - EnableSequence [ - cdev "hw:PCH" - - cset "name='Speaker Playback Switch' off" - cset "name='Headphone Playback Switch' on" - ] - DisableSequence [ - cdev "hw:PCH" - - cset "name='Headphone Playback Switch' off" - cset "name='Speaker Playback Switch' on" - ] -} - -SectionDevice."Mic".0 { - Value { - JackName "Mic Jack" - } - EnableSequence [ - cdev "hw:PCH" - - #cset "name='Capture Source' 1" - ] - DisableSequence [ - cdev "hw:PCH" - - cset "name='Capture Source' 0" - ] -} - -SectionModifier."RecordMedia".0 { - SupportedDevice [ - "Headphone" - ] - EnableSequence [ - cdev "hw:PCH" - ] - - DisableSequence [ - cdev "hw:PCH" - ] - - TransitionSequence."ToModifierName" [ - cdev "hw:PCH" - ] - - # Optional TQ and ALSA PCMs - Value { - TQ Voice - CapturePCM "hw:1" - PlaybackVolume "name='Master Playback Volume',index=2" - PlaybackSwitch "name='Master Playback Switch',index=2" - } - -} - diff --git a/conf.d/alsa-sample/ucm.sample/README b/conf.d/alsa-sample/ucm.sample/README deleted file mode 100644 index e7f08ae..0000000 --- a/conf.d/alsa-sample/ucm.sample/README +++ /dev/null @@ -1,2 +0,0 @@ -Should match sound card name ex: "HDA Intel PCH" -cp -r . /usr/share/alsa/ucm diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake index 4719486..9293781 100644 --- a/conf.d/cmake/config.cmake +++ b/conf.d/cmake/config.cmake @@ -73,7 +73,7 @@ set (PKG_REQUIRED_LIST add_compile_options(-DCONTROL_DOSCRIPT_PRE="doscript") add_compile_options(-DCONTROL_CONFIG_PRE="onload") add_compile_options(-DCONTROL_CONFIG_POST="control" ) - add_compile_options(-DCONTROL_CONFIG_PATH="${CMAKE_SOURCE_DIR}/conf.d/project/config.d:${CMAKE_INSTALL_PREFIX}/controler/config.d") + add_compile_options(-DCONTROL_CONFIG_PATH="${CMAKE_SOURCE_DIR}/conf.d/project/config.d:${CMAKE_INSTALL_PREFIX}/controller/config.d") set(CONTROL_SUPPORT_LUA 1 CACHE BOOL "Active or not LUA Support") @@ -81,13 +81,13 @@ set (PKG_REQUIRED_LIST set(LUA_COMPILER "luac5.3" CACHE STRING "LUA syntaxe check") add_compile_options(-DCONTROL_SUPPORT_LUA) add_compile_options(-DCONTROL_LUA_EVENT="luaevt") - add_compile_options(-DCONTROL_LUA_PATH="${CMAKE_SOURCE_DIR}/conf.d/project/lua.d:${CMAKE_INSTALL_PREFIX}/controler/ctl-lua.d") + add_compile_options(-DCONTROL_LUA_PATH="${CMAKE_SOURCE_DIR}/conf.d/project/lua.d:${CMAKE_INSTALL_PREFIX}/controller/ctl-lua.d") else(CONTROL_SUPPORT_LUA) message(STATUS "Warning: LUA Without Support ") endif(CONTROL_SUPPORT_LUA) - set (CTL_PLUGIN_PRE "ctl-" CACHE STRING "Prefix for Controler share plugin") - set (CTL_PLUGIN_EXT ".ctlso" CACHE STRING "Postfix for Controler share plugin") + set (CTL_PLUGIN_PRE "ctl-" CACHE STRING "Prefix for Controller share plugin") + set (CTL_PLUGIN_EXT ".ctlso" CACHE STRING "Postfix for Controller share plugin") add_compile_options(-DCTL_PLUGIN_MAGIC=2468013579) add_compile_options(-DCONTROL_PLUGIN_PATH="${CMAKE_BINARY_DIR}:${BINDINGS_INSTALL_DIR}/ctlplug:/usr/lib/afb/ctlplug") diff --git a/conf.d/project/alsa.d/asoundrc.sample b/conf.d/project/alsa.d/asoundrc.sample new file mode 100644 index 0000000..b208a09 --- /dev/null +++ b/conf.d/project/alsa.d/asoundrc.sample @@ -0,0 +1,146 @@ +# +# Author: Fulup Ar Foll +# Object: PCM hook type +# +# Test : Note: Jabra_USB=hw:v1340 +# Check SoundCard speaker-test -Dhw:v1340 -c2 -twav +# Check MixerPCM speaker-test -DMyMixerPCM -c2 -twav +# Check HookPCM speaker-test -DMyNavigationHook -c2 -twav +# Check NavPCM speaker-test -DMyNavPCM -c2 -twav +# MultiMedia aplay -DDMyNavPCM /usr/share/sounds/alsa/test.wav +# +# Bug/Feature: when softvol control is initialised from plugin and not +# from AGL binding. At 1st run ctl has invalid TLV and cannot be +# use. Bypass Solution: +# * start audio-binder before playing sound (binding create control before softvol plugin) +# * run a dummy aplay -DMyNavPCM "" to get a clean control +# +# References: https://www.spinics.net/lists/alsa-devel/msg54235.html +# -------------------------------------------------------------------- + +# Mixer PCM allow to play multiple stream simultaneously +# ------------------------------------------------------ +pcm.MyMixerPCM { + type dmix + ipc_key 1024 + ipc_key_add_uid false + ipc_perm 0666 # mixing for all users + + # Define target effective sound card (cannot be a plugin) + slave { + pcm "hw:v1340" #Jabra Solmate + period_time 0 + period_size 1024 + buffer_size 8192 + rate 44100 + } + + # DMIX can only map two channels + bindings { + 0 0 + 1 1 + } +} + +# Define a Hook_type with a private sharelib +# ------------------------------------------- +pcm_hook_type.MyHookPlugin { + install "AlsaInstallHook" + lib "/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build/Alsa-Plugin/Alsa-Policy-Hook/policy_hook_cb.so" +} + + +# Define a HookedPCM that point to Hook_type sharelib +# ---------------------------------------------------- +pcm.MyNavigationHook { + type hooks + slave.pcm "MyMixerPCM" + # Defined used hook sharelib and provide arguments/config to install func + hooks.0 { + type "MyHookPlugin" + hook_args { + verbose true # print few log messages (default false); + + # Every Call should return OK in order PCM to open (default timeout 100ms) + uri "ws://localhost:1234/api?token='audio-agent-token'" + request { + # Request autorisation to write on navigation + RequestNavigation { + api "control" + verb "dispatch" + query "{'target':'navigation', 'args':{'device':'Jabra SOLEMATE v1.34.0'}}" + } + } + # map event reception to self generated signal + event { + pause 30 + resume 31 + stop 3 + } + } + } +} + +# If hardware does not support mixer emulate it with softvol +# ----------------------------------------------------------- +pcm.MyMultimediaPCM { + type softvol + + # Point Slave on HOOK for policies control + slave.pcm "MyNavigationHook" + + # resolution=HAL(valMax+1) (default=256) + resolution 256 + + # name should match with HAL but do not set card=xx + control.name "Playback Navigation" + + # Make this plugin visible from aplay -L + hint { + show on + description "Navigation SolftVol PCM" + } +} + +# If hardware does not support mixer emulate it with softvol +# ----------------------------------------------------------- +pcm.MyNavPCM { + type softvol + + # Point Slave on HOOK for policies control + slave.pcm "MyNavigationHook" + + # resolution=HAL(valMax+1) (default=256) + resolution 256 + + # name should match with HAL but do not set card=xx + control.name "Playback Navigation" + + # Make this plugin visible from aplay -L + hint { + show on + description "Navigation SolftVol PCM" + } +} + +# If hardware does not support mixer emulate it with softvol +# ----------------------------------------------------------- +pcm.MyAlarmPCM { + type softvol + + # Point Slave on HOOK for policies control + slave.pcm "MyNavigationHook" + + # resolution=HAL(valMax+1) (default=256) + resolution 256 + + # name should match with HAL but do not set card=xx + control.name "Playback Navigation" + + # Make this plugin visible from aplay -L + hint { + show on + description "Navigation SolftVol PCM" + } +} + diff --git a/conf.d/project/alsa.d/ucm.sample/HDA Intel PCH.conf b/conf.d/project/alsa.d/ucm.sample/HDA Intel PCH.conf new file mode 100644 index 0000000..f6608a0 --- /dev/null +++ b/conf.d/project/alsa.d/ucm.sample/HDA Intel PCH.conf @@ -0,0 +1,6 @@ +Comment "Leon internal card" + +SectionUseCase."HiFi" { + File "HiFi.conf" + Comment "Default" +} diff --git a/conf.d/project/alsa.d/ucm.sample/HiFi.conf b/conf.d/project/alsa.d/ucm.sample/HiFi.conf new file mode 100644 index 0000000..9a53c8c --- /dev/null +++ b/conf.d/project/alsa.d/ucm.sample/HiFi.conf @@ -0,0 +1,84 @@ +SectionVerb { + EnableSequence [ + cdev "hw:PCH" + + cset "name='Master Playback Switch' on" + cset "name='Headphone Playback Switch' off" + cset "name='Speaker Playback Switch' on" + + cset "name='Capture Switch' on" + cset "name='Capture Volume' 39" + cset "name='Mic Boost Volume' 2" + cset "name='Internal Mic Boost Volume' 0" + #cset "name='Capture Source' 0" + ] + DisableSequence [ + ] + Value { + TQ "Music" + OutputDspName "speaker_eq" + PlaybackPCM "hw:PCH,0" + } +} + +SectionDevice."Headphone".0 { + Value { + JackName "Headphone Jack" + OutputDspName "Jheadphone" + } + EnableSequence [ + cdev "hw:PCH" + + cset "name='Speaker Playback Switch' off" + cset "name='Headphone Playback Switch' on" + ] + DisableSequence [ + cdev "hw:PCH" + + cset "name='Headphone Playback Switch' off" + cset "name='Speaker Playback Switch' on" + ] +} + +SectionDevice."Mic".0 { + Value { + JackName "Mic Jack" + } + EnableSequence [ + cdev "hw:PCH" + + #cset "name='Capture Source' 1" + ] + DisableSequence [ + cdev "hw:PCH" + + cset "name='Capture Source' 0" + ] +} + +SectionModifier."RecordMedia".0 { + SupportedDevice [ + "Headphone" + ] + EnableSequence [ + cdev "hw:PCH" + ] + + DisableSequence [ + cdev "hw:PCH" + ] + + TransitionSequence."ToModifierName" [ + cdev "hw:PCH" + ] + + # Optional TQ and ALSA PCMs + Value { + TQ Voice + CapturePCM "hw:1" + PlaybackVolume "name='Master Playback Volume',index=2" + PlaybackSwitch "name='Master Playback Switch',index=2" + } + +} + diff --git a/conf.d/project/alsa.d/ucm.sample/README b/conf.d/project/alsa.d/ucm.sample/README new file mode 100644 index 0000000..e7f08ae --- /dev/null +++ b/conf.d/project/alsa.d/ucm.sample/README @@ -0,0 +1,2 @@ +Should match sound card name ex: "HDA Intel PCH" +cp -r . /usr/share/alsa/ucm diff --git a/conf.d/project/config.d/onload-audio-control.json b/conf.d/project/config.d/onload-audio-control.json index 7180bd5..77a8fce 100644 --- a/conf.d/project/config.d/onload-audio-control.json +++ b/conf.d/project/config.d/onload-audio-control.json @@ -9,7 +9,7 @@ "label": "onload-default", "info": "onload initialisation config", "plugin": { - "label" : "MyPlug", + "label" : "_MyPlug", "sharelib": "ctl-audio-plugin-sample.ctlso", "lua2c": ["Lua2cHelloWorld1", "Lua2cHelloWorld2"] }, @@ -28,11 +28,11 @@ "info": "Assert AlsaCore Presence", "api": "alsacore", "verb": "ping", - "args": "test" + "args": {"data": "none"} }, { "label": "onload-hal-lua", "info": "Load avaliable HALs", - "lua": "Audio_Init_Hal" + "lua": "_Alsa_Get_Hal" } ] }], @@ -43,20 +43,20 @@ "actions": { "label": "multimedia-control-lua", "info": "Call Lua Script function Test_Lua_Engin", - "lua": "Audio_Set_Multimedia" + "lua": "_Audio_Set_Multimedia" } }, { "label": "navigation", "actions": { "label": "navigation-control-lua", "info": "Call Lua Script to set Navigation", - "lua": "Audio_Set_Navigation" + "lua": "_Audio_Set_Navigation" } }, { "label": "emergency", "actions": { "label": "emergency-control-ucm", - "lua": "Audio_Set_Emergency" + "lua": "_Audio_Set_Emergency" } }, { "label": "multi-step-sample", @@ -79,7 +79,7 @@ }, { "label": "navigation-control-lua", "info": "Call Lua Script to set Navigation", - "lua": "Audio_Set_Navigation" + "lua": "_Audio_Set_Navigation" }] } ], diff --git a/conf.d/project/lua.d/doscript-helloworld.lua b/conf.d/project/lua.d/doscript-helloworld.lua index 64bc94a..a06c3db 100644 --- a/conf.d/project/lua.d/doscript-helloworld.lua +++ b/conf.d/project/lua.d/doscript-helloworld.lua @@ -21,7 +21,7 @@ --]] -function helloworld (request, query) +local function helloworld (request, query) AFB:notice ("LUA HelloWorld: Simple test query=%s", query); diff --git a/conf.d/project/lua.d/onload-audio-0utils.lua b/conf.d/project/lua.d/onload-audio-0utils.lua new file mode 100644 index 0000000..b8ecd7e --- /dev/null +++ b/conf.d/project/lua.d/onload-audio-0utils.lua @@ -0,0 +1,86 @@ +--[[ + Copyright (C) 2016 "IoT.bzh" + Author Fulup Ar Foll + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Note: this file should be called before any other to assert declare function + is loaded before anything else. + + References: + http://lua-users.org/wiki/DetectingUndefinedVariables + +--]] + + +--=================================================== +--= Niklas Frykholm +-- basically if user tries to create global variable +-- the system will not let them!! +-- call GLOBAL_lock(_G) +-- +--=================================================== +function GLOBAL_lock(t) + local mt = getmetatable(t) or {} + mt.__newindex = lock_new_index + setmetatable(t, mt) +end + +--=================================================== +-- call GLOBAL_unlock(_G) +-- to change things back to normal. +--=================================================== +function GLOBAL_unlock(t) + local mt = getmetatable(t) or {} + mt.__newindex = unlock_new_index + setmetatable(t, mt) +end + +function lock_new_index(t, k, v) + if (string.sub(k,1,1) ~= "_") then + GLOBAL_unlock(_G) + error("GLOBALS are locked -- " .. k .. + " must be declared local or prefix with '_' for globals.", 2) + else + rawset(t, k, v) + end +end + +function unlock_new_index(t, k, v) + rawset(t, k, v) +end + +-- return serialised version of printable table +function Dump_Table(o) + if type(o) == 'table' then + local s = '{ ' + for k,v in pairs(o) do + if type(k) ~= 'number' then k = '"'..k..'"' end + s = s .. '['..k..'] = ' .. Dump_Table(v) .. ',' + end + return s .. '} ' + else + return tostring(o) + end +end + + +-- simulate C prinf function +printf = function(s,...) + io.write(s:format(...)) + io.write("\n") + return +end + +-- lock global variable +GLOBAL_lock(_G) diff --git a/conf.d/project/lua.d/onload-audio-controls.lua b/conf.d/project/lua.d/onload-audio-controls.lua index 4849632..24c4f71 100644 --- a/conf.d/project/lua.d/onload-audio-controls.lua +++ b/conf.d/project/lua.d/onload-audio-controls.lua @@ -18,54 +18,145 @@ Provide sample policy function for AGL Advance Audio Agent --]] -function Audio_Init_CB (status, result, context) - print ("--inlua:Audio_Init_CB-- result=", Dump_Table(result)) - print ("--inlua:Audio_Init_CB-- context=", Dump_Table(context)) - - AFB:notice("Audio_Init_Hal_CB result='%s' context='%s'", result, context) +-- Global HAL registry +_Audio_Hal_Registry={} + +-- Callback when receiving HAL registry +function _Alsa_Get_Hal_CB (error, result, context) + -- Initialise an empty table + local registry={} + + -- Only process when response is valid + if (error) then + AFB_ErrOr ("[Audio_Init_CB] ErrOr result=%s", result) + return + end + + -- Extract response from result + local response=result["response"] + + -- Index HAL Bindings APIs by shortname + for key,value in pairs(response) do + registry[value["shortname"]]=value["api"] + end + + -- store Exiting HAL for further use + printf ("-- [Audio_Init_CB] -- Audio_register_Hal=%s", Dump_Table(registry)) + _Audio_Hal_Registry=registry end -- Function call at binding load time -function Audio_Init_Hal(args, query) +function _Alsa_Get_Hal(args) - local nested = { - ["next1"]=1234, - ["next2"]="nested", - ["next3"]=9999, - } - local context = { - ["arg1"]=1234, - ["arg2"]=nested, - ["arg3"]=5678, - } + printf ("[-- Audio_Get_Hal --] args=%s", Dump_Table(argsT)) + + -- Query AlsaCore for Active HALs (no query, no context) + AFB:service ('alsacore', 'hallist', {}, "_Alsa_Get_Hal_CB", {}) + +end - print ("--inlua:Audio_Init-- response=", Dump_Table(responseT)) +-- In sample configuration Query/Args parsing is common to all audio control +local function Audio_Parse_Request (source, args, query) - print("myplug=", Dump_Table(MyPlug)); + local apihal={} - -- This routine is defined in C sample plugin - local status=MyPlug:Lua2cHelloWorld1(nested) - - -- query asynchronously loaded HAL - AFB:service ('alsacore', 'hallist', {}, "Audio_Init_CB", context) - + -- In this test we expect targeted device to be given from query (could come for args as well) + if (query == nil ) then + AFB:error ("--LUA:Audio_Set_Navigation query should contain and args with targeted apihal|device") + return -- refuse control + end + + -- Alsa Hook plugin asound sample config provides target sound card by name + if (query["device"] ~= nil) then + apihal=_Audio_Hal_Registry[query["device"]] + end + + -- HTML5 test page provides directly HAL api. + if (query["apihal"] ~= nil) then + apihal= query["apihal"] + end + + -- if requested HAL is not found then deny the control + if (apihal == nil) then + AFB:error ("--LUA:Audio_Set_Navigation No Active HAL Found") + return -- refuse control + end + + -- return api or nil when not found + return apihal end -function Audio_Set_Navigation(args, query) +-- Set Navigation lower sound when play +function _Audio_Set_Navigation(source, args, query) + + -- in strict mode every variables should be declared + local err=0 + local ctlhal={} + local response={} + local apihal={} + + AFB:notice ("LUA:Audio_Set_Use_Case source=%d args=%s query=%s", source, args, query); + + -- Parse Query/Args and if HAL not found then refuse access + apihal= Audio_Parse_Request (source, args, query) + if (apihal == nil) then return 1 end + - AFB:notice ("LUA:Audio_Set_Use_Case args=%s query=%s", args, query); + -- if source < 0 then Alsa HookPlugin is closing PCM + if (source < 0) then + -- Ramp Up Multimedia channel synchronously + ctlhal={['label']='Master_Playback_Volume', ['val']=100} + err, response= AFB:servsync (apihal, 'ctlset',ctlhal) + else + -- Ramp Down Multimedia channel synchronously + ctlhal={['label']='Master_Playback_Volume', ['val']=50} + err, response= AFB:servsync (apihal, 'ctlset',ctlhal) + end - -- synchronous call to alsacore service - local error,data= AFB:callsync ('alsacore', 'ping', {}) - if (error) then - AFB:error ("LUA:Audio_Set_Use_Case FAIL args=%s", args) - else - AFB:notice ("--LUA:Audio_Set_Use_Case DONE args=%s response=%s", args, data["response"]) + if (err) then + AFB:error("--LUA:Audio_Set_Navigation halapi=%s refuse ctl=%s", apihal, ctlhal) + return 1 -- control refused end - -- return OK - return 0 + + return 0 -- control accepted end +-- Select Multimedia mode +function _Audio_Set_Multimedia (source, args, query) + + -- in strict mode every variables should be declared + local err=0 + local ctlhal={} + local response={} + local apihal={} + + AFB:notice ("LUA:Audio_Set_Use_Case source=%d args=%s query=%s", source, args, query); + + -- Parse Query/Args and if HAL not found then refuse access + apihal= Audio_Parse_Request (source, args, query) + if (apihal == nil) then return 1 end + + + -- if Mumtimedia control only increase volume on open + if (source >= 0) then + -- Ramp Down Multimedia channel synchronously + ctlhal={['label']='Master_Playback_Volume', ['val']=100} + err, response= AFB:servsync (apihal, 'ctlset',ctlhal) + end + + if (err) then + AFB:error("--LUA:Audio_Set_Navigation halapi=%s refuse ctl=%s", apihal, ctlhal) + return 1 -- control refused + end + + + return 0 -- control accepted +end + +-- Select Emergency Mode +function _Audio_Set_Emergency(source, args, query) + return 1 -- Always refuse in this test +end diff --git a/conf.d/project/lua.d/onload-audio-oncall.lua b/conf.d/project/lua.d/onload-audio-oncall.lua index 08b25e5..23b538e 100644 --- a/conf.d/project/lua.d/onload-audio-oncall.lua +++ b/conf.d/project/lua.d/onload-audio-oncall.lua @@ -18,18 +18,18 @@ Provide sample LUA routines to be used with AGL control "lua_docall" API --]] --- global counter to keep track of calls -count=0 +--global counter to keep track of calls +_count=0 -- Display receive arguments and echo them to caller -function Simple_Echo_Args (request, args) - count=count+1 +function _Simple_Echo_Args (request, args) + _count=_count+1 AFB:notice("LUA OnCall Echo Args count=%d args=%s", count, args) print ("--inlua-- args=", Dump_Table(args)) local response={ - ["count"]=count, + ["count"]=_count, ["args"]=args, } @@ -37,7 +37,7 @@ function Simple_Echo_Args (request, args) AFB:success (request, {["func"]="Simple_Echo_Args", ["ret1"]=5678, ["ret2"]="abcd"}) end -function Test_Async_CB (request, result, context) +local function Test_Async_CB (request, result, context) response={ ["result"]=result, ["context"]=context, @@ -47,7 +47,7 @@ function Test_Async_CB (request, result, context) AFB:success (request, response) end -function Test_Call_Async (request, args) +function _Test_Call_Async (request, args) local context={ ["value1"]="abcd", ["value2"]=1234 @@ -57,7 +57,7 @@ function Test_Call_Async (request, args) AFB:service("alsacore","ping", "Test_Async_CB", context) end -function Test_Call_Sync (request, args) +function _Test_Call_Sync (request, args) AFB:notice ("Test_Call_Sync args=%s", args) local err, response= AFB:service_sync ("alsacore","ping", args) diff --git a/conf.d/project/lua.d/onload-audio-timer.lua b/conf.d/project/lua.d/onload-audio-timer.lua index 5f25de7..db7a937 100644 --- a/conf.d/project/lua.d/onload-audio-timer.lua +++ b/conf.d/project/lua.d/onload-audio-timer.lua @@ -19,11 +19,10 @@ --]] -- Create event on Lua script load -print ("*** Create MyTestEvent ***") -MyEventHandle= AFB:evtmake("MyTestEvent") +local MyEventHandle=AFB:evtmake("MyTestEvent") -- Call count time every delay/ms -function Timer_Test_CB (timer, context) +local function Timer_Test_CB (timer, context) local evtinfo= AFB:timerget(timer) print ("timer=", Dump_Table(evtinfo)) @@ -37,7 +36,7 @@ function Timer_Test_CB (timer, context) end -- sendback event depending on count and delay -function Simple_Timer_Test (request, args) +function _Simple_Timer_Test (request, args) local context = { ["info"]="My 1st private Event", @@ -68,6 +67,3 @@ function Simple_Timer_Test (request, args) return 0 end - - - diff --git a/conf.d/project/lua.d/onload-audio-utils.lua b/conf.d/project/lua.d/onload-audio-utils.lua deleted file mode 100644 index 40ef8a3..0000000 --- a/conf.d/project/lua.d/onload-audio-utils.lua +++ /dev/null @@ -1,32 +0,0 @@ ---[[ - Copyright (C) 2016 "IoT.bzh" - Author Fulup Ar Foll - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - ---]] - - --- return serialised version of printable table -function Dump_Table(o) - if type(o) == 'table' then - local s = '{ ' - for k,v in pairs(o) do - if type(k) ~= 'number' then k = '"'..k..'"' end - s = s .. '['..k..'] = ' .. Dump_Table(v) .. ',' - end - return s .. '} ' - else - return tostring(o) - end -end diff --git a/data/default-control-policy.json b/data/default-control-policy.json index 30cd1ce..c6f8bd0 100644 --- a/data/default-control-policy.json +++ b/data/default-control-policy.json @@ -6,7 +6,7 @@ "version": "1.0" }, "onload": { - "info": "controler initialisation config", + "info": "controller initialisation config", "plugin": "sample-audio-policy.so", "actions": [ { diff --git a/htdocs/AudioBinding.js b/htdocs/AudioBinding.js index 607d31c..4d14600 100644 --- a/htdocs/AudioBinding.js +++ b/htdocs/AudioBinding.js @@ -94,7 +94,6 @@ var selectobj = document.getElementById(elemid); if (!selectobj) { - console.log ("****** elemid=%s does not exit in HTML page ****", elemid); return; } diff --git a/htdocs/alsa-hal.html b/htdocs/alsa-hal.html index f1d5aaf..013c547 100644 --- a/htdocs/alsa-hal.html +++ b/htdocs/alsa-hal.html @@ -27,15 +27,15 @@
    -
  1. -
  2. -
  3. -
  4. +
  5. +
  6. +
  7. +

  8. -
  9. -
  10. -
  11. -
  12. +
  13. +
  14. +
  15. +

  16. diff --git a/htdocs/audio-control.html b/htdocs/audio-control.html index d51ae55..6b7872f 100644 --- a/htdocs/audio-control.html +++ b/htdocs/audio-control.html @@ -5,14 +5,15 @@ - - + -

    Simple Audio Control test

    - +

    Simple Audio Control Test

    +

    - + Selected HAL + + API Verbosity
    -
    +
      -
    1. -
    2. -
    3. +
    4. +
    5. +

    6. -
    7. +
    8. -
    9. +
    diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml index a5d9f8e..235c3b0 100644 --- a/nbproject/configurations.xml +++ b/nbproject/configurations.xml @@ -43,7 +43,7 @@ AudioCommonLib.c - + ctl-binding.c ctl-dispatch.c ctl-lua.c @@ -234,7 +234,7 @@ - + ../../../opt/include @@ -243,15 +243,15 @@ /usr/include/json-c /usr/include/lua5.3 Audio-Common - build/Controler-afb + build/Controller-afb - CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controler/config.d" + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" CONTROL_CONFIG_POST="control" CONTROL_CONFIG_PRE="onload" CONTROL_DOSCRIPT_PRE="doscript" CONTROL_LUA_EVENT="luaevt" - CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controler/ctl-lua.d" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" CONTROL_MAXPATH_LEN=255 CONTROL_ONLOAD_DEFAULT="onload-default" CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" @@ -265,79 +265,60 @@ - + ../../../opt/include/afb - Controler-afb + Controller-afb /usr/include/json-c /usr/include/lua5.3 Audio-Common ../../../opt/include - build/Controler-afb + build/Controller-afb - - + + - ../../../opt/include - ../../../opt/include/alsa - /usr/include/p11-kit-1 + ../../../opt/include/afb + Controller-afb /usr/include/json-c /usr/include/lua5.3 Audio-Common - build/Controler-afb + ../../../opt/include + build/Controller-afb - - CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controler/config.d" - CONTROL_CONFIG_POST="control" - CONTROL_CONFIG_PRE="onload" - CONTROL_DOSCRIPT_PRE="doscript" - CONTROL_LUA_EVENT="luaevt" - CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controler/ctl-lua.d" - CONTROL_MAXPATH_LEN=255 - CONTROL_ONLOAD_DEFAULT="onload-default" - CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" - CONTROL_SUPPORT_LUA - CTL_PLUGIN_MAGIC=2468013579 - MAX_LINEAR_DB_SCALE=24 - MAX_SND_CARD=16 - NATIVE_LINUX - TLV_BYTE_SIZE=256 - control_afb_EXPORTS - - + ../../../opt/include/afb - Controler-afb + Controller-afb /usr/include/json-c - build/Controler-afb + build/Controller-afb - + ../../../opt/include/afb - Controler-afb + Controller-afb /usr/include/json-c /usr/include/lua5.3 - build/Controler-afb + build/Controller-afb - + ../../../opt/include/afb - Controler-afb - /usr/include/json-c + Controller-afb ../../../opt/include - build/Controler-afb + build/Controller-afb @@ -462,12 +443,12 @@ build/Alsa-Plugin/Alsa-Policy-Hook - CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controler/config.d" + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" CONTROL_CONFIG_POST="control" CONTROL_CONFIG_PRE="onload" CONTROL_DOSCRIPT_PRE="doscript" CONTROL_LUA_EVENT="luaevt" - CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controler/ctl-lua.d" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" CONTROL_MAXPATH_LEN=255 CONTROL_ONLOAD_DEFAULT="onload-default" CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" @@ -499,7 +480,7 @@ /usr/include/json-c /usr/include/lua5.3 Audio-Common - build/Controler-afb + build/Controller-afb build/HAL-afb/HAL-plugin /usr/include/alsa build/Common @@ -507,11 +488,11 @@ CONTROL_CDEV_RX="/dev/inic-usb-crx" CONTROL_CDEV_TX="/dev/inic-usb-ctx" - CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controler/config.d" + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" CONTROL_CONFIG_POST="control" CONTROL_CONFIG_PRE="onload" CONTROL_LUA_EVENT="luaevt" - CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controler/ctl-lua.d" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" CONTROL_MAXPATH_LEN=255 CONTROL_ONLOAD_DEFAULT="onload-default" CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" @@ -534,11 +515,11 @@ /usr/include/lua5.3 - CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controler/config.d" + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" CONTROL_CONFIG_POST="control" CONTROL_CONFIG_PRE="onload" CONTROL_LUA_EVENT="luaevt" - CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controler/ctl-lua.d" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" CONTROL_MAXPATH_LEN=255 CONTROL_ONLOAD_DEFAULT="onload-default" CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" @@ -561,7 +542,7 @@ Audio-Common - build/Controler-afb + build/Controller-afb build/HAL-afb/HAL-plugin ../../../opt/include/afb HAL-afb/HAL-plugin @@ -611,7 +592,7 @@ Audio-Common - build/Controler-afb + build/Controller-afb build/HAL-afb/HAL-plugin ../../../opt/include/afb HAL-afb/Unicens-USB @@ -633,7 +614,7 @@ /usr/include/json-c /usr/include/lua5.3 Audio-Common - build/Controler-afb + build/Controller-afb build/HAL-afb/HAL-plugin ../../../opt/include/afb HighLevel-afb @@ -641,11 +622,11 @@ build/HighLevel-afb - CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controler/config.d" + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" CONTROL_CONFIG_POST="control" CONTROL_CONFIG_PRE="onload" CONTROL_LUA_EVENT="luaevt" - CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controler/ctl-lua.d" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" CONTROL_MAXPATH_LEN=255 CONTROL_ONLOAD_DEFAULT="onload-default" CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" @@ -667,15 +648,15 @@ /usr/include/json-c /usr/include/lua5.3 Audio-Common - build/Controler-afb + build/Controller-afb build/HAL-afb/HAL-plugin - CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controler/config.d" + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" CONTROL_CONFIG_POST="control" CONTROL_CONFIG_PRE="onload" CONTROL_LUA_EVENT="luaevt" - CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controler/ctl-lua.d" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" CONTROL_MAXPATH_LEN=255 CONTROL_ONLOAD_DEFAULT="onload-default" CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" @@ -1058,7 +1039,8 @@ - + @@ -1105,16 +1087,16 @@ ../../../opt/include/alsa /usr/include/json-c - Shared-Interface + Audio-Common ../../../opt/include - build/ALSA-afb + build/Alsa-afb - build/ALSA-afb + build/Alsa-afb @@ -1123,9 +1105,9 @@ ../../../opt/include/alsa /usr/include/json-c - Shared-Interface + Audio-Common ../../../opt/include - build/ALSA-afb + build/Alsa-afb @@ -1134,9 +1116,9 @@ ../../../opt/include/alsa /usr/include/json-c - Shared-Interface + Audio-Common ../../../opt/include - build/ALSA-afb + build/Alsa-afb @@ -1145,28 +1127,76 @@ ../../../opt/include/alsa /usr/include/json-c - Shared-Interface + Audio-Common ../../../opt/include - build/ALSA-afb + build/Alsa-afb + + ../../../opt/include/afb + Audio-Common + /usr/include/json-c + build/Audio-Common + + + + + + + ../../../opt/include/afb + Audio-Common + /usr/include/json-c + build/Audio-Common + + + + + + + Audio-Common + /usr/include/json-c + build/Audio-Common + - + + + + control_afb_EXPORTS + + + + + + + control_afb_EXPORTS + + + + + + control_afb_EXPORTS + - + + + audio_plugin_sample_EXPORTS + - + + + control_afb_EXPORTS + - Shared-Interface + Audio-Common ../../../opt/include build/HAL-afb/HAL-interface @@ -1187,7 +1217,7 @@ flavor2="3"> - Shared-Interface + Audio-Common build/HAL-afb/HAL-interface @@ -1220,7 +1250,7 @@ - + Shared-Interface HAL-afb/HAL-interface @@ -1230,7 +1260,7 @@ - + HAL-afb/HAL-interface build/HAL-afb/Unicens-USB @@ -1300,15 +1330,16 @@ build/Alsa-Plugin/Alsa-Policy-Hook - CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controler/config.d" + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" CONTROL_CONFIG_POST="control" CONTROL_CONFIG_PRE="onload" CONTROL_DOSCRIPT_PRE="doscript" CONTROL_LUA_EVENT="luaevt" - CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controler/ctl-lua.d" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" CONTROL_MAXPATH_LEN=255 CONTROL_ONLOAD_DEFAULT="onload-default" CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" + CONTROL_SUPPORT_LUA CTL_PLUGIN_MAGIC=2468013579 MAX_LINEAR_DB_SCALE=24 MAX_SND_CARD=16 @@ -1323,21 +1354,11 @@ ../../../opt/include/afb - ALSA-afb - - - - - - - ../../../opt/include/afb - Shared-Interface - /usr/include/json-c - build/Shared-Interface + Alsa-afb - + ../../../opt/include @@ -1346,24 +1367,24 @@ /usr/include/json-c /usr/include/lua5.3 Audio-Common - build/Controler-afb + build/Controller-afb - CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controler/config.d" + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" CONTROL_CONFIG_POST="control" CONTROL_CONFIG_PRE="onload" CONTROL_DOSCRIPT_PRE="doscript" CONTROL_LUA_EVENT="luaevt" - CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controler/ctl-lua.d" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" CONTROL_MAXPATH_LEN=255 CONTROL_ONLOAD_DEFAULT="onload-default" CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" + CONTROL_SUPPORT_LUA CTL_PLUGIN_MAGIC=2468013579 MAX_LINEAR_DB_SCALE=24 MAX_SND_CARD=16 NATIVE_LINUX TLV_BYTE_SIZE=256 - control_afb_EXPORTS @@ -1384,7 +1405,7 @@ HAL-afb/HDA-intel ../../../opt/include/alsa /usr/include/json-c - Shared-Interface + Audio-Common HAL-afb/HAL-interface ../../../opt/include build/HAL-afb/HDA-intel @@ -1395,10 +1416,10 @@ HAL-afb/Jabra-Solemate + ../../../opt/include/afb ../../../opt/include/alsa /usr/include/json-c - ../../../opt/include/afb - Shared-Interface + Audio-Common HAL-afb/HAL-interface ../../../opt/include build/HAL-afb/Jabra-Solemate @@ -1412,7 +1433,7 @@ HAL-afb/Scarlett-Focusrite ../../../opt/include/alsa /usr/include/json-c - Shared-Interface + Audio-Common HAL-afb/HAL-interface ../../../opt/include build/HAL-afb/Scarlett-Focusrite @@ -1462,11 +1483,11 @@ /usr/include/lua5.3 - CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controler/config.d" + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" CONTROL_CONFIG_POST="control" CONTROL_CONFIG_PRE="onload" CONTROL_LUA_EVENT="luaevt" - CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controler/ctl-lua.d" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" CONTROL_MAXPATH_LEN=255 CONTROL_ONLOAD_DEFAULT="onload-default" CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" @@ -1522,65 +1543,65 @@ - + Audio-Common - build/Controler-afb + build/Controller-afb control_afb_EXPORTS - + Audio-Common - build/Controler-afb + build/Controller-afb control_afb_EXPORTS - + Audio-Common - build/Controler-afb + build/Controller-afb control_afb_EXPORTS - + Audio-Common - build/Controler-afb + build/Controller-afb control_afb_EXPORTS - + - build/Controler-afb + build/Controller-afb audio_plugin_sample_EXPORTS - + Audio-Common - build/Controler-afb + build/Controller-afb control_afb_EXPORTS @@ -1725,7 +1746,7 @@ Audio-Common - build/Controler-afb + build/Controller-afb build/HAL-afb/HAL-plugin /usr/include/alsa build/Common @@ -1738,7 +1759,7 @@ - + CONTROL_DOSCRIPT_PRE="doscript" @@ -1761,7 +1782,7 @@ Audio-Common - build/Controler-afb + build/Controller-afb build/HAL-afb/HAL-plugin ../../../opt/include/afb HAL-afb/HAL-plugin @@ -1817,7 +1838,7 @@ Audio-Common - build/Controler-afb + build/Controller-afb build/HAL-afb/HAL-plugin ../../../opt/include/afb HAL-afb/Unicens-USB @@ -1834,7 +1855,7 @@ Audio-Common - build/Controler-afb + build/Controller-afb build/HAL-afb/HAL-plugin ../../../opt/include/afb HighLevel-afb @@ -1851,7 +1872,7 @@ Audio-Common - build/Controler-afb + build/Controller-afb build/HAL-afb/HAL-plugin @@ -1861,5 +1882,685 @@ + + + GNU|GNU + false + false + + + + + + + + + + true + + + + build + ${MAKE} -f Makefile install + ${MAKE} -f Makefile clean + build/CMakeFiles/feature_tests.bin + + + + + build + cmake .. + true + + + + + + + + + + ../../../opt/include/afb + Alsa-afb + ../../../opt/include/alsa + /usr/include/json-c + Audio-Common + ../../../opt/include + build/Alsa-afb + + + + + + + ../../../opt/include + ../../../opt/include/alsa + /usr/include/p11-kit-1 + /usr/include/json-c + /usr/include/lua5.3 + Audio-Common + build/Alsa-afb + + + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" + CONTROL_CONFIG_POST="control" + CONTROL_CONFIG_PRE="onload" + CONTROL_DOSCRIPT_PRE="doscript" + CONTROL_LUA_EVENT="luaevt" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" + CONTROL_MAXPATH_LEN=255 + CONTROL_ONLOAD_DEFAULT="onload-default" + CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" + CONTROL_SUPPORT_LUA + CTL_PLUGIN_MAGIC=2468013579 + MAX_LINEAR_DB_SCALE=24 + MAX_SND_CARD=16 + NATIVE_LINUX + TLV_BYTE_SIZE=256 + alsa_lowlevel_EXPORTS + + + + + + + ../../../opt/include/afb + Alsa-afb + ../../../opt/include/alsa + /usr/include/json-c + Audio-Common + ../../../opt/include + build/Alsa-afb + + + + + + + ../../../opt/include/afb + Alsa-afb + ../../../opt/include/alsa + /usr/include/json-c + Audio-Common + ../../../opt/include + build/Alsa-afb + + + + + + + ../../../opt/include/afb + Alsa-afb + ../../../opt/include/alsa + /usr/include/json-c + Audio-Common + ../../../opt/include + build/Alsa-afb + + + + + + + ../../../opt/include/afb + Audio-Common + /usr/include/json-c + build/Audio-Common + + + + + + + ../../../opt/include + ../../../opt/include/alsa + /usr/include/p11-kit-1 + /usr/include/json-c + /usr/include/lua5.3 + Audio-Common + build/Audio-Common + + + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" + CONTROL_CONFIG_POST="control" + CONTROL_CONFIG_PRE="onload" + CONTROL_DOSCRIPT_PRE="doscript" + CONTROL_LUA_EVENT="luaevt" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" + CONTROL_MAXPATH_LEN=255 + CONTROL_ONLOAD_DEFAULT="onload-default" + CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" + CONTROL_SUPPORT_LUA + CTL_PLUGIN_MAGIC=2468013579 + MAX_LINEAR_DB_SCALE=24 + MAX_SND_CARD=16 + NATIVE_LINUX + TLV_BYTE_SIZE=256 + + + + + + + Audio-Common + /usr/include/json-c + build/Audio-Common + + + + + + + + + + + ../../../opt/include + ../../../opt/include/alsa + /usr/include/p11-kit-1 + /usr/include/json-c + /usr/include/lua5.3 + Audio-Common + build/Controller-afb + + + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" + CONTROL_CONFIG_POST="control" + CONTROL_CONFIG_PRE="onload" + CONTROL_DOSCRIPT_PRE="doscript" + CONTROL_LUA_EVENT="luaevt" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" + CONTROL_MAXPATH_LEN=255 + CONTROL_ONLOAD_DEFAULT="onload-default" + CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" + CONTROL_SUPPORT_LUA + CTL_PLUGIN_MAGIC=2468013579 + MAX_LINEAR_DB_SCALE=24 + MAX_SND_CARD=16 + NATIVE_LINUX + TLV_BYTE_SIZE=256 + control_afb_EXPORTS + + + + + + + ../../../opt/include + ../../../opt/include/alsa + /usr/include/p11-kit-1 + /usr/include/json-c + /usr/include/lua5.3 + Audio-Common + build/Controller-afb + + + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" + CONTROL_CONFIG_POST="control" + CONTROL_CONFIG_PRE="onload" + CONTROL_DOSCRIPT_PRE="doscript" + CONTROL_LUA_EVENT="luaevt" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" + CONTROL_MAXPATH_LEN=255 + CONTROL_ONLOAD_DEFAULT="onload-default" + CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" + CONTROL_SUPPORT_LUA + CTL_PLUGIN_MAGIC=2468013579 + MAX_LINEAR_DB_SCALE=24 + MAX_SND_CARD=16 + NATIVE_LINUX + TLV_BYTE_SIZE=256 + control_afb_EXPORTS + + + + + + + ../../../opt/include + ../../../opt/include/alsa + /usr/include/p11-kit-1 + /usr/include/json-c + /usr/include/lua5.3 + Audio-Common + build/Controller-afb + + + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" + CONTROL_CONFIG_POST="control" + CONTROL_CONFIG_PRE="onload" + CONTROL_DOSCRIPT_PRE="doscript" + CONTROL_LUA_EVENT="luaevt" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" + CONTROL_MAXPATH_LEN=255 + CONTROL_ONLOAD_DEFAULT="onload-default" + CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" + CONTROL_SUPPORT_LUA + CTL_PLUGIN_MAGIC=2468013579 + MAX_LINEAR_DB_SCALE=24 + MAX_SND_CARD=16 + NATIVE_LINUX + TLV_BYTE_SIZE=256 + control_afb_EXPORTS + + + + + + + ../../../opt/include/afb + Controller-afb + /usr/include/json-c + build/Controller-afb + + + + + + + ../../../opt/include + ../../../opt/include/alsa + /usr/include/p11-kit-1 + /usr/include/json-c + /usr/include/lua5.3 + Audio-Common + build/Controller-afb + + + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" + CONTROL_CONFIG_POST="control" + CONTROL_CONFIG_PRE="onload" + CONTROL_DOSCRIPT_PRE="doscript" + CONTROL_LUA_EVENT="luaevt" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" + CONTROL_MAXPATH_LEN=255 + CONTROL_ONLOAD_DEFAULT="onload-default" + CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" + CONTROL_SUPPORT_LUA + CTL_PLUGIN_MAGIC=2468013579 + MAX_LINEAR_DB_SCALE=24 + MAX_SND_CARD=16 + NATIVE_LINUX + TLV_BYTE_SIZE=256 + audio_plugin_sample_EXPORTS + + + + + + + ../../../opt/include + ../../../opt/include/alsa + /usr/include/p11-kit-1 + /usr/include/json-c + /usr/include/lua5.3 + Audio-Common + build/Controller-afb + + + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" + CONTROL_CONFIG_POST="control" + CONTROL_CONFIG_PRE="onload" + CONTROL_DOSCRIPT_PRE="doscript" + CONTROL_LUA_EVENT="luaevt" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" + CONTROL_MAXPATH_LEN=255 + CONTROL_ONLOAD_DEFAULT="onload-default" + CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" + CONTROL_SUPPORT_LUA + CTL_PLUGIN_MAGIC=2468013579 + MAX_LINEAR_DB_SCALE=24 + MAX_SND_CARD=16 + NATIVE_LINUX + TLV_BYTE_SIZE=256 + control_afb_EXPORTS + + + + + + + Audio-Common + build/HAL-afb/HAL-interface + + + + + + + Audio-Common + build/HAL-afb/HAL-interface + + + + + + + build/HAL-afb/HAL-interface + + + + + + + + + + + + + + + + + + + + + + + Shared-Interface + HAL-afb/HAL-interface + build/HAL-afb/Unicens-USB + + + + + + + HAL-afb/HAL-interface + build/HAL-afb/Unicens-USB + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../opt/include + ../../../opt/include/alsa + /usr/include/p11-kit-1 + /usr/include/json-c + /usr/include/lua5.3 + build/Alsa-Plugin/Alsa-Policy-Hook + + + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" + CONTROL_CONFIG_POST="control" + CONTROL_CONFIG_PRE="onload" + CONTROL_DOSCRIPT_PRE="doscript" + CONTROL_LUA_EVENT="luaevt" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" + CONTROL_MAXPATH_LEN=255 + CONTROL_ONLOAD_DEFAULT="onload-default" + CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" + CONTROL_SUPPORT_LUA + CTL_PLUGIN_MAGIC=2468013579 + MAX_LINEAR_DB_SCALE=24 + MAX_SND_CARD=16 + NATIVE_LINUX + PIC + TLV_BYTE_SIZE=256 + policy_hook_cb_EXPORTS + + + + + + + ../../../opt/include + ../../../opt/include/alsa + /usr/include/p11-kit-1 + /usr/include/json-c + /usr/include/lua5.3 + Audio-Common + build/Controller-afb + build/HAL-afb/HAL-plugin + /usr/include/alsa + build/Common + + + CONTROL_CDEV_RX="/dev/inic-usb-crx" + CONTROL_CDEV_TX="/dev/inic-usb-ctx" + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" + CONTROL_CONFIG_POST="control" + CONTROL_CONFIG_PRE="onload" + CONTROL_LUA_EVENT="luaevt" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" + CONTROL_MAXPATH_LEN=255 + CONTROL_ONLOAD_DEFAULT="onload-default" + CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" + CTL_PLUGIN_MAGIC=2468013579 + MAX_LINEAR_DB_SCALE=24 + MAX_SND_CARD=16 + TLV_BYTE_SIZE=256 + audio_plugin_sample_EXPORTS + control_afb_EXPORTS + + + + + + + ../../../opt/include + ../../../opt/include/alsa + /usr/include/p11-kit-1 + /usr/include/json-c + /usr/include/lua5.3 + + + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" + CONTROL_CONFIG_POST="control" + CONTROL_CONFIG_PRE="onload" + CONTROL_LUA_EVENT="luaevt" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" + CONTROL_MAXPATH_LEN=255 + CONTROL_ONLOAD_DEFAULT="onload-default" + CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" + CTL_PLUGIN_MAGIC=2468013579 + MAX_LINEAR_DB_SCALE=24 + MAX_SND_CARD=16 + TLV_BYTE_SIZE=256 + + + + + + + ../../../opt/include/afb + HAL-afb/HAL-interface + + + + + + + Audio-Common + build/Controller-afb + build/HAL-afb/HAL-plugin + ../../../opt/include/afb + HAL-afb/HAL-plugin + + + CONTROL_CDEV_RX="/dev/inic-usb-crx" + CONTROL_CDEV_TX="/dev/inic-usb-ctx" + audio_plugin_sample_EXPORTS + control_afb_EXPORTS + + + + + + + ../../../opt/include/afb + HAL-afb/HDA-intel + Audio-Common + HAL-afb/HAL-interface + build/HAL-afb/HDA-intel + + + + + + + HAL-afb/Jabra-Solemate + ../../../opt/include/afb + Audio-Common + HAL-afb/HAL-interface + build/HAL-afb/Jabra-Solemate + + + + + + + ../../../opt/include/afb + HAL-afb/Scarlett-Focusrite + Audio-Common + HAL-afb/HAL-interface + build/HAL-afb/Scarlett-Focusrite + + + + + + + Audio-Common + build/Controller-afb + build/HAL-afb/HAL-plugin + ../../../opt/include/afb + HAL-afb/Unicens-USB + + + CONTROL_CDEV_RX="/dev/inic-usb-crx" + CONTROL_CDEV_TX="/dev/inic-usb-ctx" + audio_plugin_sample_EXPORTS + control_afb_EXPORTS + + + + + + + ../../../opt/include + ../../../opt/include/alsa + /usr/include/p11-kit-1 + /usr/include/json-c + /usr/include/lua5.3 + Audio-Common + build/Controller-afb + build/HAL-afb/HAL-plugin + ../../../opt/include/afb + HighLevel-afb + Shared-Interface + build/HighLevel-afb + + + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" + CONTROL_CONFIG_POST="control" + CONTROL_CONFIG_PRE="onload" + CONTROL_LUA_EVENT="luaevt" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" + CONTROL_MAXPATH_LEN=255 + CONTROL_ONLOAD_DEFAULT="onload-default" + CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" + CTL_PLUGIN_MAGIC=2468013579 + MAX_LINEAR_DB_SCALE=24 + MAX_SND_CARD=16 + TLV_BYTE_SIZE=256 + audio_plugin_sample_EXPORTS + control_afb_EXPORTS + + + + + + + ../../../opt/include + ../../../opt/include/alsa + /usr/include/p11-kit-1 + /usr/include/json-c + /usr/include/lua5.3 + Audio-Common + build/Controller-afb + build/HAL-afb/HAL-plugin + + + CONTROL_CONFIG_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/config.d:/usr/local/controller/config.d" + CONTROL_CONFIG_POST="control" + CONTROL_CONFIG_PRE="onload" + CONTROL_LUA_EVENT="luaevt" + CONTROL_LUA_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/conf.d/project/lua.d:/usr/local/controller/ctl-lua.d" + CONTROL_MAXPATH_LEN=255 + CONTROL_ONLOAD_DEFAULT="onload-default" + CONTROL_PLUGIN_PATH="/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build:/home/fulup/opt/audio-bindings/ctlplug:/usr/lib/afb/ctlplug" + CTL_PLUGIN_MAGIC=2468013579 + MAX_LINEAR_DB_SCALE=24 + MAX_SND_CARD=16 + TLV_BYTE_SIZE=256 + audio_plugin_sample_EXPORTS + control_afb_EXPORTS + + + + diff --git a/nbproject/project.xml b/nbproject/project.xml index 7c62c94..db3bdab 100644 --- a/nbproject/project.xml +++ b/nbproject/project.xml @@ -29,6 +29,10 @@ LocalHostNoTrace 0 + + Local_Raw_Controller + 0 + false -- cgit 1.2.3-korg