diff options
37 files changed, 3024 insertions, 533 deletions
diff --git a/ALSA-afb/Alsa-ApiHat.h b/ALSA-afb/Alsa-ApiHat.h index 6e50e11..b05cfbe 100644 --- a/ALSA-afb/Alsa-ApiHat.h +++ b/ALSA-afb/Alsa-ApiHat.h @@ -22,7 +22,7 @@ #include <alsa/asoundlib.h> #include <systemd/sd-event.h> -#include "audio-interface.h" +#include "audio-common.h" typedef enum { ACTION_SET, diff --git a/ALSA-afb/CMakeLists.txt b/ALSA-afb/CMakeLists.txt index 443acbd..876e837 100644 --- a/ALSA-afb/CMakeLists.txt +++ b/ALSA-afb/CMakeLists.txt @@ -32,7 +32,7 @@ PROJECT_TARGET_ADD(alsa-lowlevel) # Library dependencies (include updates automatically) TARGET_LINK_LIBRARIES(${TARGET_NAME} - audio-interface + audio-common ${link_libraries} ) diff --git a/Alsa-Plugin/Alsa-Policy-Hook/PolicyHookCb.c b/Alsa-Plugin/Alsa-Policy-Hook/PolicyHookCb.c index ae2a5a3..501ebf9 100644 --- a/Alsa-Plugin/Alsa-Policy-Hook/PolicyHookCb.c +++ b/Alsa-Plugin/Alsa-Policy-Hook/PolicyHookCb.c @@ -62,7 +62,18 @@ #define UNUSED_FUNCTION(x) __attribute__((__unused__)) UNUSED_ ## x void OnRequestCB(void* UNUSED(handle) , const char* UNUSED(api), const char* UNUSED(verb), struct afb_wsj1_msg*UNUSED(msg)) {} - typedef struct { +typedef struct { + const char *api; + const char *verb; + long timeout; + char *query; + + sd_event_source *evtSource; + char *callIdTag; + void *afbClient; +} afbRequestT; + +typedef struct { snd_pcm_t *pcm; const char *uri; struct afb_wsj1 *wsj1; @@ -71,18 +82,10 @@ void OnRequestCB(void* UNUSED(handle) , const char* UNUSED(api), const char* UNU sem_t semaphore; int count; int error; + afbRequestT **request; } afbClientT; -typedef struct { - const char *api; - const char *verb; - long timeout; - char *query; - - sd_event_source *evtSource; - char *callIdTag; - afbClientT *afbClient; -} afbRequestT; + static void *LoopInThread(void *handle) { afbClientT *afbClient = (afbClientT*) handle; @@ -110,6 +113,11 @@ static void OnHangupCB(void *handle, struct afb_wsj1 *wsj1) { if (err) exit(1); } +typedef enum { + HOOK_INSTALL, + HOOK_CLOSE, +} hookActionT; + // supported action typedef enum { ACTION_PAUSE, @@ -201,8 +209,9 @@ static struct afb_wsj1_itf itf = { void OnResponseCB(void *handle, struct afb_wsj1_msg *msg) { afbRequestT *afbRequest= (afbRequestT*)handle; + afbClientT *afbClient=(afbClientT*)afbRequest->afbClient; - if (afbRequest->afbClient->verbose) printf("ON-RESPONSE call=%s response=%s\n", afbRequest->callIdTag, afb_wsj1_msg_object_s(msg)); + if (afbClient->verbose) printf("ON-RESPONSE call=%s response=%s\n", afbRequest->callIdTag, afb_wsj1_msg_object_s(msg)); // Cancel timeout for this request sd_event_source_unref(afbRequest->evtSource); @@ -210,18 +219,18 @@ void OnResponseCB(void *handle, struct afb_wsj1_msg *msg) { if (! afb_wsj1_msg_is_reply_ok(msg)) goto OnErrorExit; // When not more waiting call release semaphore - afbRequest->afbClient->count--; - if (afbRequest->afbClient->count == 0) { - if (afbRequest->afbClient->verbose) printf("ON-RESPONSE No More Waiting Request\n"); - afbRequest->afbClient->error=0; - sem_post (&afbRequest->afbClient->semaphore); + afbClient->count--; + if (afbClient->count == 0) { + if (afbClient->verbose) printf("ON-RESPONSE No More Waiting Request\n"); + afbClient->error=0; + sem_post (&afbClient->semaphore); } return; OnErrorExit: fprintf(stderr, "ON-RESPONSE ERROR call=%s response=%s\n", afbRequest->callIdTag, afb_wsj1_msg_object_s(msg)); - afbRequest->afbClient->error=1; - sem_post (&afbRequest->afbClient->semaphore); + afbClient->error=1; + sem_post (&afbClient->semaphore); } int OnTimeoutCB (sd_event_source* source, uint64_t timer, void* handle) { @@ -237,9 +246,10 @@ int OnTimeoutCB (sd_event_source* source, uint64_t timer, void* handle) { } // Call AGL binder asynchronously by with a timeout -static int CallWithTimeout(afbClientT *afbClient, afbRequestT *afbRequest, int count) { +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); @@ -248,7 +258,9 @@ 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); - err = afb_wsj1_call_s(afbClient->wsj1, afbRequest->api, afbRequest->verb, afbRequest->query, OnResponseCB, afbRequest); + 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 (err) goto OnErrorExit; // save client handle in request @@ -261,37 +273,40 @@ OnErrorExit: return -1; } -static int LaunchCallRequest(afbClientT *afbClient, afbRequestT **afbRequest) { +static int LaunchCallRequest(afbClientT *afbClient, hookActionT action) { pthread_t tid; int err, idx; + afbRequestT **afbRequest= afbClient->request; + + if (action == HOOK_INSTALL) { + // init waiting counting semaphore + if (sem_init(&afbClient->semaphore, 1, 0) == -1) { + fprintf(stderr, "LaunchCallRequest: Fail Semaphore Init: %s\n", afbClient->uri); + } - // init waiting counting semaphore - if (sem_init(&afbClient->semaphore, 1, 0) == -1) { - fprintf(stderr, "LaunchCallRequest: Fail Semaphore Init: %s\n", afbClient->uri); - } - - // Create a main loop - err = sd_event_default(&afbClient->sdLoop); - if (err < 0) { - fprintf(stderr, "LaunchCallRequest: Connection to default event loop failed: %s\n", strerror(-err)); - goto OnErrorExit; + // Create a main loop + err = sd_event_default(&afbClient->sdLoop); + if (err < 0) { + fprintf(stderr, "LaunchCallRequest: Connection to default event loop failed: %s\n", strerror(-err)); + goto OnErrorExit; + } + + // start a thread with a mainloop to monitor Audio-Agent + err = pthread_create(&tid, NULL, &LoopInThread, afbClient); + if (err) goto OnErrorExit; + + // connect the websocket wsj1 to the uri given by the first argument + afbClient->wsj1 = afb_ws_client_connect_wsj1(afbClient->sdLoop, afbClient->uri, &itf, afbClient); + if (afbClient->wsj1 == NULL) { + fprintf(stderr, "LaunchCallRequest: Connection to %s failed\n", afbClient->uri); + goto OnErrorExit; + } } - // start a thread with a mainloop to monitor Audio-Agent - err = pthread_create(&tid, NULL, &LoopInThread, afbClient); - if (err) goto OnErrorExit; - - // connect the websocket wsj1 to the uri given by the first argument - afbClient->wsj1 = afb_ws_client_connect_wsj1(afbClient->sdLoop, afbClient->uri, &itf, afbClient); - if (afbClient->wsj1 == NULL) { - fprintf(stderr, "LaunchCallRequest: Connection to %s failed\n", afbClient->uri); - goto OnErrorExit; - } - // send call request to audio-agent asynchronously (respond with thread mainloop context) for (idx = 0; afbRequest[idx] != NULL; idx++) { - err = CallWithTimeout(afbClient, afbRequest[idx], idx); + err = CallWithTimeout(afbClient, afbRequest[idx], idx, action); if (err) { fprintf(stderr, "LaunchCallRequest: Fail call %s//%s/%s&%s", afbClient->uri, afbRequest[idx]->api, afbRequest[idx]->verb, afbRequest[idx]->query); goto OnErrorExit; @@ -311,7 +326,25 @@ static int AlsaCloseHook(snd_pcm_hook_t *hook) { afbClientT *afbClient = (afbClientT*) snd_pcm_hook_get_private (hook); - if (afbClient->verbose) fprintf(stdout, "\nAlsaCloseHook pcm=%s\n", snd_pcm_name(afbClient->pcm)); + // launch call request and create a waiting mainloop thread + int err = LaunchCallRequest(afbClient, HOOK_CLOSE); + if (err < 0) { + fprintf (stderr, "PCM Fail to Enter Mainloop\n"); + goto OnErrorExit; + } + + // wait for all call request to return + sem_wait(&afbClient->semaphore); + if (afbClient->error) { + fprintf (stderr, "AlsaCloseHook: Audio Agent Fail to respond\n"); + goto OnErrorExit; + } + + if (afbClient->verbose) fprintf(stdout, "\nAlsaHook Close Success PCM=%s URI=%s\n", snd_pcm_name(afbClient->pcm), afbClient->uri); + return 0; + +OnErrorExit: + fprintf(stderr, "\nAlsaPcmHook Plugin Close Fail PCM=%s\n", snd_pcm_name(afbClient->pcm)); return 0; } @@ -327,7 +360,7 @@ int PLUGIN_ENTRY_POINT (snd_pcm_t *pcm, snd_config_t *conf) { // start populating client handle afbClient->pcm = pcm; afbClient->verbose = 0; - + afbClient->request = afbRequest; // Get PCM arguments from asoundrc snd_config_for_each(it, next, conf) { @@ -393,18 +426,22 @@ int PLUGIN_ENTRY_POINT (snd_pcm_t *pcm, snd_config_t *conf) { err = snd_config_search(ctlconfig, "api", &itemConf); if (!err) { - if (snd_config_get_string(itemConf, &afbRequest[callCount]->api) < 0) { + const char *api; + if (snd_config_get_string(itemConf, &api) < 0) { SNDERR("Invalid api string for %s", callLabel); goto OnErrorExit; } + afbRequest[callCount]->api=strdup(api); } err = snd_config_search(ctlconfig, "verb", &itemConf); if (!err) { - if (snd_config_get_string(itemConf, &afbRequest[callCount]->verb) < 0) { + const char *verb; + if (snd_config_get_string(itemConf, &verb) < 0) { SNDERR("Invalid verb string %s", id); goto OnErrorExit; } + afbRequest[callCount]->verb=strdup(verb); } err = snd_config_search(ctlconfig, "timeout", &itemConf); @@ -462,7 +499,7 @@ int PLUGIN_ENTRY_POINT (snd_pcm_t *pcm, snd_config_t *conf) { if (err < 0) goto OnErrorExit; // launch call request and create a waiting mainloop thread - err = LaunchCallRequest(afbClient, afbRequest); + err = LaunchCallRequest(afbClient, HOOK_INSTALL); if (err < 0) { fprintf (stderr, "PCM Fail to Enter Mainloop\n"); goto OnErrorExit; diff --git a/Shared-Interface/CMakeLists.txt b/Audio-Common/CMakeLists.txt index 107a0cb..7544fe6 100644 --- a/Shared-Interface/CMakeLists.txt +++ b/Audio-Common/CMakeLists.txt @@ -17,10 +17,10 @@ ########################################################################### # Add target to project dependency list -PROJECT_TARGET_ADD(audio-interface) +PROJECT_TARGET_ADD(audio-common) # Define targets - ADD_LIBRARY(${TARGET_NAME} STATIC audio-interface.c) + ADD_LIBRARY(${TARGET_NAME} STATIC audio-common.c wrap-json.c) # Library properties SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES diff --git a/Shared-Interface/audio-interface.c b/Audio-Common/audio-common.c index 3386460..ae41dde 100644 --- a/Shared-Interface/audio-interface.c +++ b/Audio-Common/audio-common.c @@ -16,7 +16,7 @@ * */ #define _GNU_SOURCE // needed for vasprintf -#include "audio-interface.h" +#include "audio-common.h" PUBLIC const char *halCtlsLabels[] = { diff --git a/Shared-Interface/audio-interface.h b/Audio-Common/audio-common.h index 7bf258a..7bf258a 100644 --- a/Shared-Interface/audio-interface.h +++ b/Audio-Common/audio-common.h diff --git a/Audio-Common/wrap-json.c b/Audio-Common/wrap-json.c new file mode 100644 index 0000000..164e127 --- /dev/null +++ b/Audio-Common/wrap-json.c @@ -0,0 +1,939 @@ +/* + Copyright (C) 2016, 2017 "IoT.bzh" + + author: José Bollo <jose.bollo@iot.bzh> + + 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. +*/ + +#include <string.h> + +#include "wrap-json.h" + +#define STACKCOUNT 32 +#define STRCOUNT 8 + +enum { + wrap_json_error_none, + wrap_json_error_null_object, + wrap_json_error_truncated, + wrap_json_error_internal_error, + wrap_json_error_out_of_memory, + wrap_json_error_invalid_character, + wrap_json_error_too_long, + wrap_json_error_too_deep, + wrap_json_error_null_spec, + wrap_json_error_null_key, + wrap_json_error_null_string, + wrap_json_error_out_of_range, + wrap_json_error_incomplete, + wrap_json_error_missfit_type, + wrap_json_error_key_not_found, + _wrap_json_error_count_ +}; + +static const char ignore_all[] = " \t\n\r,:"; +static const char pack_accept_arr[] = "][{snbiIfoO"; +static const char pack_accept_key[] = "s}"; +#define pack_accept_any (&pack_accept_arr[1]) + +static const char unpack_accept_arr[] = "*!][{snbiIfFoO"; +static const char unpack_accept_key[] = "*!s}"; +#define unpack_accept_any (&unpack_accept_arr[3]) + +static const char *pack_errors[_wrap_json_error_count_] = +{ + [wrap_json_error_none] = "unknown error", + [wrap_json_error_null_object] = "null object", + [wrap_json_error_truncated] = "truncated", + [wrap_json_error_internal_error] = "internal error", + [wrap_json_error_out_of_memory] = "out of memory", + [wrap_json_error_invalid_character] = "invalid character", + [wrap_json_error_too_long] = "too long", + [wrap_json_error_too_deep] = "too deep", + [wrap_json_error_null_spec] = "spec is NULL", + [wrap_json_error_null_key] = "key is NULL", + [wrap_json_error_null_string] = "string is NULL", + [wrap_json_error_out_of_range] = "array too small", + [wrap_json_error_incomplete] = "incomplete container", + [wrap_json_error_missfit_type] = "missfit of type", + [wrap_json_error_key_not_found] = "key not found" +}; + +int wrap_json_get_error_position(int rc) +{ + if (rc < 0) + rc = -rc; + return (rc >> 4) + 1; +} + +int wrap_json_get_error_code(int rc) +{ + if (rc < 0) + rc = -rc; + return rc & 15; +} + +const char *wrap_json_get_error_string(int rc) +{ + rc = wrap_json_get_error_code(rc); + if (rc >= sizeof pack_errors / sizeof *pack_errors) + rc = 0; + return pack_errors[rc]; +} + + + +static inline const char *skip(const char *d) +{ + while (*d && strchr(ignore_all, *d)) + d++; + return d; +} + +int wrap_json_vpack(struct json_object **result, const char *desc, va_list args) +{ + /* TODO: the case of structs with key being single char should be optimized */ + int nstr, notnull, nullable, rc; + size_t sz, dsz, ssz; + char *s; + char c; + const char *d; + char buffer[256]; + struct { const char *str; size_t sz; } strs[STRCOUNT]; + struct { struct json_object *cont, *key; const char *acc; char type; } stack[STACKCOUNT], *top; + struct json_object *obj; + + ssz = sizeof buffer; + s = buffer; + top = stack; + top->key = NULL; + top->cont = NULL; + top->acc = pack_accept_any; + top->type = 0; + d = desc; + if (!d) + goto null_spec; + d = skip(d); + for(;;) { + c = *d; + if (!c) + goto truncated; + if (!strchr(top->acc, c)) + goto invalid_character; + d = skip(++d); + switch(c) { + case 's': + nullable = 0; + notnull = 0; + nstr = 0; + sz = 0; + for (;;) { + strs[nstr].str = va_arg(args, const char*); + if (strs[nstr].str) + notnull = 1; + if (*d == '?') { + d = skip(++d); + nullable = 1; + } + switch(*d) { + case '%': strs[nstr].sz = va_arg(args, size_t); d = skip(++d); break; + case '#': strs[nstr].sz = (size_t)va_arg(args, int); d = skip(++d); break; + default: strs[nstr].sz = strs[nstr].str ? strlen(strs[nstr].str) : 0; break; + } + sz += strs[nstr++].sz; + if (*d == '?') { + d = skip(++d); + nullable = 1; + } + if (*d != '+') + break; + if (nstr >= STRCOUNT) + goto too_long; + d = skip(++d); + } + if (*d == '*') + nullable = 1; + if (notnull) { + if (sz > ssz) { + ssz += ssz; + if (ssz < sz) + ssz = sz; + s = alloca(sz); + } + dsz = sz; + while (nstr) { + nstr--; + dsz -= strs[nstr].sz; + memcpy(&s[dsz], strs[nstr].str, strs[nstr].sz); + } + obj = json_object_new_string_len(s, (int)sz); + if (!obj) + goto out_of_memory; + } else if (nullable) + obj = NULL; + else + goto null_string; + break; + case 'n': + obj = NULL; + break; + case 'b': + obj = json_object_new_boolean(va_arg(args, int)); + if (!obj) + goto out_of_memory; + break; + case 'i': + obj = json_object_new_int(va_arg(args, int)); + if (!obj) + goto out_of_memory; + break; + case 'I': + obj = json_object_new_int64(va_arg(args, int64_t)); + if (!obj) + goto out_of_memory; + break; + case 'f': + obj = json_object_new_double(va_arg(args, double)); + if (!obj) + goto out_of_memory; + break; + case 'o': + case 'O': + obj = va_arg(args, struct json_object*); + if (*d == '?') + d = skip(++d); + else if (*d != '*' && !obj) + goto null_object; + if (c == 'O') + json_object_get(obj); + break; + case '[': + case '{': + if (++top >= &stack[STACKCOUNT]) + goto too_deep; + top->key = NULL; + if (c == '[') { + top->type = ']'; + top->acc = pack_accept_arr; + top->cont = json_object_new_array(); + } else { + top->type = '}'; + top->acc = pack_accept_key; + top->cont = json_object_new_object(); + } + if (!top->cont) + goto out_of_memory; + continue; + case '}': + case ']': + if (c != top->type || top <= stack) + goto internal_error; + obj = (top--)->cont; + if (*d == '*' && !(c == '}' ? json_object_object_length(obj) : json_object_array_length(obj))) { + json_object_put(obj); + obj = NULL; + } + break; + default: + goto internal_error; + } + switch (top->type) { + case 0: + if (top != stack) + goto internal_error; + if (*d) + goto invalid_character; + *result = obj; + return 0; + case ']': + if (obj || *d != '*') + json_object_array_add(top->cont, obj); + if (*d == '*') + d = skip(++d); + break; + case '}': + if (!obj) + goto null_key; + top->key = obj; + top->acc = pack_accept_any; + top->type = ':'; + break; + case ':': + if (obj || *d != '*') + json_object_object_add(top->cont, json_object_get_string(top->key), obj); + if (*d == '*') + d = skip(++d); + json_object_put(top->key); + top->key = NULL; + top->acc = pack_accept_key; + top->type = '}'; + break; + default: + goto internal_error; + } + } + +null_object: + rc = wrap_json_error_null_object; + goto error; +truncated: + rc = wrap_json_error_truncated; + goto error; +internal_error: + rc = wrap_json_error_internal_error; + goto error; +out_of_memory: + rc = wrap_json_error_out_of_memory; + goto error; +invalid_character: + rc = wrap_json_error_invalid_character; + goto error; +too_long: + rc = wrap_json_error_too_long; + goto error; +too_deep: + rc = wrap_json_error_too_deep; + goto error; +null_spec: + rc = wrap_json_error_null_spec; + goto error; +null_key: + rc = wrap_json_error_null_key; + goto error; +null_string: + rc = wrap_json_error_null_string; + goto error; +error: + do { + json_object_put(top->key); + json_object_put(top->cont); + } while (--top >= stack); + *result = NULL; + rc = rc | (int)((d - desc) << 4); + return -rc; +} + +int wrap_json_pack(struct json_object **result, const char *desc, ...) +{ + int rc; + va_list args; + + va_start(args, desc); + rc = wrap_json_vpack(result, desc, args); + va_end(args); + return rc; +} + +static int vunpack(struct json_object *object, const char *desc, va_list args, int store) +{ + int rc = 0, optionnal, ignore; + char c, xacc[2] = { 0, 0 }; + const char *acc; + const char *d, *fit = NULL; + const char *key = NULL; + const char **ps = NULL; + double *pf = NULL; + int *pi = NULL; + int64_t *pI = NULL; + size_t *pz = NULL; + struct { struct json_object *parent; const char *acc; int index, count; char type; } stack[STACKCOUNT], *top; + struct json_object *obj; + struct json_object **po; + + xacc[0] = 0; + ignore = 0; + top = NULL; + acc = unpack_accept_any; + d = desc; + if (!d) + goto null_spec; + d = skip(d); + obj = object; + for(;;) { + fit = d; + c = *d; + if (!c) + goto truncated; + if (!strchr(acc, c)) + goto invalid_character; + d = skip(++d); + switch(c) { + case 's': + if (xacc[0] == '}') { + /* expects a key */ + key = va_arg(args, const char *); + if (!key) + goto null_key; + if (*d != '?') + optionnal = 0; + else { + optionnal = 1; + d = skip(++d); + } + if (ignore) + ignore++; + else { + if (json_object_object_get_ex(top->parent, key, &obj)) { + /* found */ + top->index++; + } else { + /* not found */ + if (!optionnal) + goto key_not_found; + ignore = 1; + obj = NULL; + } + } + xacc[0] = ':'; + acc = unpack_accept_any; + continue; + } + /* get a string */ + if (store) + ps = va_arg(args, const char **); + if (!ignore) { + if (!json_object_is_type(obj, json_type_string)) + goto missfit; + if (store && ps) + *ps = json_object_get_string(obj); + } + if (*d == '%') { + d = skip(++d); + if (store) { + pz = va_arg(args, size_t *); + if (!ignore && pz) + *pz = (size_t)json_object_get_string_len(obj); + } + } + break; + case 'n': + if (!ignore && !json_object_is_type(obj, json_type_null)) + goto missfit; + break; + case 'b': + if (store) + pi = va_arg(args, int *); + + if (!ignore) { + if (!json_object_is_type(obj, json_type_boolean)) + goto missfit; + if (store && pi) + *pi = json_object_get_boolean(obj); + } + break; + case 'i': + if (store) + pi = va_arg(args, int *); + + if (!ignore) { + if (!json_object_is_type(obj, json_type_int)) + goto missfit; + if (store && pi) + *pi = json_object_get_int(obj); + } + break; + case 'I': + if (store) + pI = va_arg(args, int64_t *); + + if (!ignore) { + if (!json_object_is_type(obj, json_type_int)) + goto missfit; + if (store && pI) + *pI = json_object_get_int64(obj); + } + break; + case 'f': + case 'F': + if (store) + pf = va_arg(args, double *); + + if (!ignore) { + if (!(json_object_is_type(obj, json_type_double) || (c == 'F' && json_object_is_type(obj, json_type_int)))) + goto missfit; + if (store && pf) + *pf = json_object_get_double(obj); + } + break; + case 'o': + case 'O': + if (store) { + po = va_arg(args, struct json_object **); + if (!ignore && po) { + if (c == 'O') + obj = json_object_get(obj); + *po = obj; + } + } + break; + + case '[': + case '{': + if (!top) + top = stack; + else if (++top >= &stack[STACKCOUNT]) + goto too_deep; + + top->acc = acc; + top->type = xacc[0]; + top->index = 0; + top->parent = obj; + if (ignore) + ignore++; + if (c == '[') { + if (!ignore) { + if (!json_object_is_type(obj, json_type_array)) + goto missfit; + top->count = json_object_array_length(obj); + } + xacc[0] = ']'; + acc = unpack_accept_arr; + } else { + if (!ignore) { + if (!json_object_is_type(obj, json_type_object)) + goto missfit; + top->count = json_object_object_length(obj); + } + xacc[0] = '}'; + acc = unpack_accept_key; + continue; + } + break; + case '}': + case ']': + if (!top || c != xacc[0]) + goto internal_error; + acc = top->acc; + xacc[0] = top->type; + top = top == stack ? NULL : top - 1; + if (ignore) + ignore--; + break; + case '!': + if (*d != xacc[0]) + goto invalid_character; + if (!ignore && top->index != top->count) + goto incomplete; + /*@fallthrough@*/ + case '*': + acc = xacc; + continue; + default: + goto internal_error; + } + switch (xacc[0]) { + case 0: + if (top) + goto internal_error; + if (*d) + goto invalid_character; + return 0; + case ']': + if (!ignore) { + key = strchr(unpack_accept_arr, *d); + if (key && key >= unpack_accept_any) { + if (top->index >= top->count) + goto out_of_range; + obj = json_object_array_get_idx(top->parent, top->index++); + } + } + break; + case ':': + acc = unpack_accept_key; + xacc[0] = '}'; + if (ignore) + ignore--; + break; + default: + goto internal_error; + } + } +truncated: + rc = wrap_json_error_truncated; + goto error; +internal_error: + rc = wrap_json_error_internal_error; + goto error; +invalid_character: + rc = wrap_json_error_invalid_character; + goto error; +too_deep: + rc = wrap_json_error_too_deep; + goto error; +null_spec: + rc = wrap_json_error_null_spec; + goto error; +null_key: + rc = wrap_json_error_null_key; + goto error; +out_of_range: + rc = wrap_json_error_out_of_range; + goto error; +incomplete: + rc = wrap_json_error_incomplete; + goto error; +missfit: + rc = wrap_json_error_missfit_type; + goto errorfit; +key_not_found: + rc = wrap_json_error_key_not_found; + goto error; +errorfit: + d = fit; +error: + rc = rc | (int)((d - desc) << 4); + return -rc; +} + +int wrap_json_vcheck(struct json_object *object, const char *desc, va_list args) +{ + return vunpack(object, desc, args, 0); +} + +int wrap_json_check(struct json_object *object, const char *desc, ...) +{ + int rc; + va_list args; + + va_start(args, desc); + rc = vunpack(object, desc, args, 0); + va_end(args); + return rc; +} + +int wrap_json_vmatch(struct json_object *object, const char *desc, va_list args) +{ + return !vunpack(object, desc, args, 0); +} + +int wrap_json_match(struct json_object *object, const char *desc, ...) +{ + int rc; + va_list args; + + va_start(args, desc); + rc = vunpack(object, desc, args, 0); + va_end(args); + return !rc; +} + +int wrap_json_vunpack(struct json_object *object, const char *desc, va_list args) +{ + return vunpack(object, desc, args, 1); +} + +int wrap_json_unpack(struct json_object *object, const char *desc, ...) +{ + int rc; + va_list args; + + va_start(args, desc); + rc = vunpack(object, desc, args, 1); + va_end(args); + return rc; +} + +static void object_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure) +{ + struct json_object_iterator it = json_object_iter_begin(object); + struct json_object_iterator end = json_object_iter_end(object); + while (!json_object_iter_equal(&it, &end)) { + callback(closure, json_object_iter_peek_value(&it), json_object_iter_peek_name(&it)); + json_object_iter_next(&it); + } +} + +static void array_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure) +{ + int n = json_object_array_length(object); + int i = 0; + while(i < n) + callback(closure, json_object_array_get_idx(object, i++)); +} + +void wrap_json_optarray_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure) +{ + if (json_object_is_type(object, json_type_array)) + array_for_all(object, callback, closure); + else + callback(closure, object); +} + +void wrap_json_array_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure) +{ + if (json_object_is_type(object, json_type_array)) + array_for_all(object, callback, closure); +} + +void wrap_json_object_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure) +{ + if (json_object_is_type(object, json_type_object)) + object_for_all(object, callback, closure); +} + +void wrap_json_optobject_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure) +{ + if (json_object_is_type(object, json_type_object)) + object_for_all(object, callback, closure); + else + callback(closure, object, NULL); +} + +void wrap_json_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure) +{ + if (!object) + /* do nothing */; + else if (json_object_is_type(object, json_type_object)) + object_for_all(object, callback, closure); + else if (!json_object_is_type(object, json_type_array)) + callback(closure, object, NULL); + else { + int n = json_object_array_length(object); + int i = 0; + while(i < n) + callback(closure, json_object_array_get_idx(object, i++), NULL); + } +} + +#if defined(WRAP_JSON_TEST) +#include <stdio.h> + +void p(const char *desc, ...) +{ + int rc; + va_list args; + struct json_object *result; + + va_start(args, desc); + rc = wrap_json_vpack(&result, desc, args); + va_end(args); + if (!rc) + printf(" SUCCESS %s\n\n", json_object_to_json_string(result)); + else + printf(" ERROR[char %d err %d] %s\n\n", wrap_json_get_error_position(rc), wrap_json_get_error_code(rc), wrap_json_get_error_string(rc)); + json_object_put(result); +} + +const char *xs[10]; +int *xi[10]; +int64_t *xI[10]; +double *xf[10]; +struct json_object *xo[10]; +size_t xz[10]; + +void u(const char *value, const char *desc, ...) +{ + unsigned m, k; + int rc; + va_list args; + struct json_object *obj, *o; + + memset(xs, 0, sizeof xs); + memset(xi, 0, sizeof xi); + memset(xI, 0, sizeof xI); + memset(xf, 0, sizeof xf); + memset(xo, 0, sizeof xo); + memset(xz, 0, sizeof xz); + obj = json_tokener_parse(value); + va_start(args, desc); + rc = wrap_json_vunpack(obj, desc, args); + va_end(args); + if (rc) + printf(" ERROR[char %d err %d] %s\n\n", wrap_json_get_error_position(rc), wrap_json_get_error_code(rc), wrap_json_get_error_string(rc)); + else { + value = NULL; + printf(" SUCCESS"); + va_start(args, desc); + k = m = 0; + while(*desc) { + switch(*desc) { + case '{': m = (m << 1) | 1; k = 1; break; + case '}': m = m >> 1; k = m&1; break; + case '[': m = m << 1; k = 0; break; + case ']': m = m >> 1; k = m&1; break; + case 's': printf(" s:%s", k ? va_arg(args, const char*) : *(va_arg(args, const char**)?:&value)); k ^= m&1; break; + case '%': printf(" %%:%zu", *va_arg(args, size_t*)); k = m&1; break; + case 'n': printf(" n"); k = m&1; break; + case 'b': printf(" b:%d", *va_arg(args, int*)); k = m&1; break; + case 'i': printf(" i:%d", *va_arg(args, int*)); k = m&1; break; + case 'I': printf(" I:%lld", *va_arg(args, int64_t*)); k = m&1; break; + case 'f': printf(" f:%f", *va_arg(args, double*)); k = m&1; break; + case 'F': printf(" F:%f", *va_arg(args, double*)); k = m&1; break; + case 'o': printf(" o:%s", json_object_to_json_string(*va_arg(args, struct json_object**))); k = m&1; break; + case 'O': o = *va_arg(args, struct json_object**); printf(" O:%s", json_object_to_json_string(o)); json_object_put(o); k = m&1; break; + default: break; + } + desc++; + } + va_end(args); + printf("\n\n"); + } + json_object_put(obj); +} + +#define P(...) do{ printf("pack(%s)\n",#__VA_ARGS__); p(__VA_ARGS__); } while(0) +#define U(...) do{ printf("unpack(%s)\n",#__VA_ARGS__); u(__VA_ARGS__); } while(0) + +int main() +{ + char buffer[4] = {'t', 'e', 's', 't'}; + + P("n"); + P("b", 1); + P("b", 0); + P("i", 1); + P("I", (uint64_t)0x123456789abcdef); + P("f", 3.14); + P("s", "test"); + P("s?", "test"); + P("s?", NULL); + P("s#", "test asdf", 4); + P("s%", "test asdf", (size_t)4); + P("s#", buffer, 4); + P("s%", buffer, (size_t)4); + P("s++", "te", "st", "ing"); + P("s#+#+", "test", 1, "test", 2, "test"); + P("s%+%+", "test", (size_t)1, "test", (size_t)2, "test"); + P("{}", 1.0); + P("[]", 1.0); + P("o", json_object_new_int(1)); + P("o?", json_object_new_int(1)); + P("o?", NULL); + P("O", json_object_new_int(1)); + P("O?", json_object_new_int(1)); + P("O?", NULL); + P("{s:[]}", "foo"); + P("{s+#+: []}", "foo", "barbar", 3, "baz"); + P("{s:s,s:o,s:O}", "a", NULL, "b", NULL, "c", NULL); + P("{s:**}", "a", NULL); + P("{s:s*,s:o*,s:O*}", "a", NULL, "b", NULL, "c", NULL); + P("[i,i,i]", 0, 1, 2); + P("[s,o,O]", NULL, NULL, NULL); + P("[**]", NULL); + P("[s*,o*,O*]", NULL, NULL, NULL); + P(" s ", "test"); + P("[ ]"); + P("[ i , i, i ] ", 1, 2, 3); + P("{\n\n1"); + P("[}"); + P("{]"); + P("["); + P("{"); + P("[i]a", 42); + P("ia", 42); + P("s", NULL); + P("+", NULL); + P(NULL); + P("{s:i}", NULL, 1); + P("{ {}: s }", "foo"); + P("{ s: {}, s:[ii{} }", "foo", "bar", 12, 13); + P("[[[[[ [[[[[ [[[[ }]]]] ]]]] ]]]]]"); + + U("true", "b", &xi[0]); + U("false", "b", &xi[0]); + U("null", "n"); + U("42", "i", &xi[0]); + U("123456789", "I", &xI[0]); + U("3.14", "f", &xf[0]); + U("12345", "F", &xf[0]); + U("3.14", "F", &xf[0]); + U("\"foo\"", "s", &xs[0]); + U("\"foo\"", "s%", &xs[0], &xz[0]); + U("{}", "{}"); + U("[]", "[]"); + U("{}", "o", &xo[0]); + U("{}", "O", &xo[0]); + U("{\"foo\":42}", "{si}", "foo", &xi[0]); + U("[1,2,3]", "[i,i,i]", &xi[0], &xi[1], &xi[2]); + U("{\"a\":1,\"b\":2,\"c\":3}", "{s:i, s:i, s:i}", "a", &xi[0], "b", &xi[1], "c", &xi[2]); + U("42", "z"); + U("null", "[i]"); + U("[]", "[}"); + U("{}", "{]"); + U("[]", "["); + U("{}", "{"); + U("[42]", "[i]a", &xi[0]); + U("42", "ia", &xi[0]); + U("[]", NULL); + U("\"foo\"", "s", NULL); + U("42", "s", NULL); + U("42", "n"); + U("42", "b", NULL); + U("42", "f", NULL); + U("42", "[i]", NULL); + U("42", "{si}", "foo", NULL); + U("\"foo\"", "n"); + U("\"foo\"", "b", NULL); + U("\"foo\"", "i", NULL); + U("\"foo\"", "I", NULL); + U("\"foo\"", "f", NULL); + U("\"foo\"", "F", NULL); + U("true", "s", NULL); + U("true", "n"); + U("true", "i", NULL); + U("true", "I", NULL); + U("true", "f", NULL); + U("true", "F", NULL); + U("[42]", "[ii]", &xi[0], &xi[1]); + U("{\"foo\":42}", "{si}", NULL, &xi[0]); + U("{\"foo\":42}", "{si}", "baz", &xi[0]); + U("[1,2,3]", "[iii!]", &xi[0], &xi[1], &xi[2]); + U("[1,2,3]", "[ii!]", &xi[0], &xi[1]); + U("[1,2,3]", "[ii]", &xi[0], &xi[1]); + U("[1,2,3]", "[ii*]", &xi[0], &xi[1]); + U("{\"foo\":42,\"baz\":45}", "{sisi}", "baz", &xi[0], "foo", &xi[1]); + U("{\"foo\":42,\"baz\":45}", "{sisi*}", "baz", &xi[0], "foo", &xi[1]); + U("{\"foo\":42,\"baz\":45}", "{sisi!}", "baz", &xi[0], "foo", &xi[1]); + U("{\"foo\":42,\"baz\":45}", "{si}", "baz", &xi[0], "foo", &xi[1]); + U("{\"foo\":42,\"baz\":45}", "{si*}", "baz", &xi[0], "foo", &xi[1]); + U("{\"foo\":42,\"baz\":45}", "{si!}", "baz", &xi[0], "foo", &xi[1]); + U("[1,{\"foo\":2,\"bar\":null},[3,4]]", "[i{sisn}[ii]]", &xi[0], "foo", &xi[1], "bar", &xi[2], &xi[3]); + U("[1,2,3]", "[ii!i]", &xi[0], &xi[1], &xi[2]); + U("[1,2,3]", "[ii*i]", &xi[0], &xi[1], &xi[2]); + U("{\"foo\":1,\"bar\":2}", "{si!si}", "foo", &xi[1], "bar", &xi[2]); + U("{\"foo\":1,\"bar\":2}", "{si*si}", "foo", &xi[1], "bar", &xi[2]); + U("{\"foo\":{\"baz\":null,\"bar\":null}}", "{s{sn!}}", "foo", "bar"); + U("[[1,2,3]]", "[[ii!]]", &xi[0], &xi[1]); + U("{}", "{s?i}", "foo", &xi[0]); + U("{\"foo\":1}", "{s?i}", "foo", &xi[0]); + U("{}", "{s?[ii]s?{s{si!}}}", "foo", &xi[0], &xi[1], "bar", "baz", "quux", &xi[2]); + U("{\"foo\":[1,2]}", "{s?[ii]s?{s{si!}}}", "foo", &xi[0], &xi[1], "bar", "baz", "quux", &xi[2]); + U("{\"bar\":{\"baz\":{\"quux\":15}}}", "{s?[ii]s?{s{si!}}}", "foo", &xi[0], &xi[1], "bar", "baz", "quux", &xi[2]); + U("{\"foo\":{\"bar\":4}}", "{s?{s?i}}", "foo", "bar", &xi[0]); + U("{\"foo\":{}}", "{s?{s?i}}", "foo", "bar", &xi[0]); + U("{}", "{s?{s?i}}", "foo", "bar", &xi[0]); + U("{\"foo\":42,\"baz\":45}", "{s?isi!}", "baz", &xi[0], "foo", &xi[1]); + U("{\"foo\":42}", "{s?isi!}", "baz", &xi[0], "foo", &xi[1]); + return 0; +} + +#endif + +#if 0 + + + /* Unpack the same item twice */ + j = json_pack("{s:s, s:i, s:b}", "foo", "bar", "baz", 42, "quux", 1); + if(!json_unpack_ex(j, &error, 0, "{s:s,s:s!}", "foo", &s, "foo", &s)) + fail("json_unpack object with strict validation failed"); + { + const char *possible_errors[] = { + "2 object item(s) left unpacked: baz, quux", + "2 object item(s) left unpacked: quux, baz" + }; + check_errors(possible_errors, 2, "<validation>", 1, 10, 10); + } + json_decref(j); + +#endif diff --git a/Audio-Common/wrap-json.h b/Audio-Common/wrap-json.h new file mode 100644 index 0000000..cb2d0bf --- /dev/null +++ b/Audio-Common/wrap-json.h @@ -0,0 +1,46 @@ +/* + Copyright (C) 2016, 2017 "IoT.bzh" + + author: José Bollo <jose.bollo@iot.bzh> + + 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. +*/ + +#pragma once + +#include <stdarg.h> +#include <json-c/json.h> + +extern int wrap_json_get_error_position(int rc); +extern int wrap_json_get_error_code(int rc); +extern const char *wrap_json_get_error_string(int rc); + +extern int wrap_json_vpack(struct json_object **result, const char *desc, va_list args); +extern int wrap_json_pack(struct json_object **result, const char *desc, ...); + +extern int wrap_json_vunpack(struct json_object *object, const char *desc, va_list args); +extern int wrap_json_unpack(struct json_object *object, const char *desc, ...); +extern int wrap_json_vcheck(struct json_object *object, const char *desc, va_list args); +extern int wrap_json_check(struct json_object *object, const char *desc, ...); +extern int wrap_json_vmatch(struct json_object *object, const char *desc, va_list args); +extern int wrap_json_match(struct json_object *object, const char *desc, ...); + +extern void wrap_json_optarray_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure); +extern void wrap_json_array_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure); + +extern void wrap_json_optarray_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure); +extern void wrap_json_array_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure); +extern void wrap_json_object_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure); +extern void wrap_json_optobject_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure); +extern void wrap_json_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure); + diff --git a/Audio-Common/wrap-json.md b/Audio-Common/wrap-json.md new file mode 100644 index 0000000..92940f1 --- /dev/null +++ b/Audio-Common/wrap-json.md @@ -0,0 +1,305 @@ +WRAP-JSON facility +================== + +The facility wrap-json is based on the pack/unpack API on the +libray jansson. The two chapters below are copied from the +documentation of jansson library copyrighted by Petri Lehtinen +(see at end). + +Building Values +--------------- + +This section describes functions that help to create, or *pack*, complex +JSON values, especially nested objects and arrays. Value building is +based on a *format string* that is used to tell the functions about the +expected arguments. + +For example, the format string `"i"` specifies a single integer value, +while the format string `"[ssb]"` or the equivalent `"[s, s, b]"` +specifies an array value with two strings and a boolean as its items: + + /* Create the JSON integer 42 */ + wrap_json_pack(&result, "i", 42); + + /* Create the JSON array ["foo", "bar", true] */ + wrap_json_pack(&result, "[ssb]", "foo", "bar", 1); + +Here's the full list of format specifiers. The type in parentheses +denotes the resulting JSON type, and the type in brackets (if any) +denotes the C type that is expected as the corresponding argument or +arguments. + +`s` (string) \[const char \*\] + +: Convert a null terminated UTF-8 string to a JSON string. + +`s?` (string) \[const char \*\] + +: Like `s`, but if the argument is *NULL*, output a JSON null value. + +`s*` (string) \[const char \*\] + +: Like `s`, but if the argument is *NULL*, do not output any value. + This format can only be used inside an object or an array. If used + inside an object, the corresponding key is additionally suppressed + when the value is omitted. See below for an example. + +`s#` (string) \[const char \*, int\] + +: Convert a UTF-8 buffer of a given length to a JSON string. + +`s%` (string) \[const char \*, size\_t\] + +: Like `s#` but the length argument is of type size\_t. + +`+` \[const char \*\] + +: Like `s`, but concatenate to the previous string. Only valid after + `s`, `s#`, `+` or `+#`. + +`+#` \[const char \*, int\] + +: Like `s#`, but concatenate to the previous string. Only valid after + `s`, `s#`, `+` or `+#`. + +`+%` (string) \[const char \*, size\_t\] + +: Like `+#` but the length argument is of type size\_t. + +`n` (null) + +: Output a JSON null value. No argument is consumed. + +`b` (boolean) \[int\] + +: Convert a C int to JSON boolean value. Zero is converted to `false` + and non-zero to `true`. + +`i` (integer) \[int\] + +: Convert a C int to JSON integer. + +`I` (integer) \[json\_int\_t\] + +: Convert a C json\_int\_t to JSON integer. + +`f` (real) \[double\] + +: Convert a C double to JSON real. + +`o` (any value) \[json\_t \*\] + +: Output any given JSON value as-is. If the value is added to an array + or object, the reference to the value passed to `o` is stolen by the + container. + +`O` (any value) \[json\_t \*\] + +: Like `o`, but the argument's reference count is incremented. This is + useful if you pack into an array or object and want to keep the + reference for the JSON value consumed by `O` to yourself. + +`o?`, `O?` (any value) \[json\_t \*\] + +: Like `o` and `O`, respectively, but if the argument is *NULL*, + output a JSON null value. + +`o*`, `O*` (any value) \[json\_t \*\] + +: Like `o` and `O`, respectively, but if the argument is *NULL*, do + not output any value. This format can only be used inside an object + or an array. If used inside an object, the corresponding key is + additionally suppressed. See below for an example. + +`[fmt]` (array) + +: Build an array with contents from the inner format string. `fmt` may + contain objects and arrays, i.e. recursive value building is + supported. + +`{fmt}` (object) + +: Build an object with contents from the inner format string `fmt`. + The first, third, etc. format specifier represent a key, and must be + a string (see `s`, `s#`, `+` and `+#` above), as object keys are + always strings. The second, fourth, etc. format specifier represent + a value. Any value may be an object or array, i.e. recursive value + building is supported. + +Whitespace, `:` and `,` are ignored. + +More examples: + + /* Build an empty JSON object */ + wrap_json_pack(&result, "{}"); + + /* Build the JSON object {"foo": 42, "bar": 7} */ + wrap_json_pack(&result, "{sisi}", "foo", 42, "bar", 7); + + /* Like above, ':', ',' and whitespace are ignored */ + wrap_json_pack(&result, "{s:i, s:i}", "foo", 42, "bar", 7); + + /* Build the JSON array [[1, 2], {"cool": true}] */ + wrap_json_pack(&result, "[[i,i],{s:b}]", 1, 2, "cool", 1); + + /* Build a string from a non-null terminated buffer */ + char buffer[4] = {'t', 'e', 's', 't'}; + wrap_json_pack(&result, "s#", buffer, 4); + + /* Concatenate strings together to build the JSON string "foobarbaz" */ + wrap_json_pack(&result, "s++", "foo", "bar", "baz"); + + /* Create an empty object or array when optional members are missing */ + wrap_json_pack(&result, "{s:s*,s:o*,s:O*}", "foo", NULL, "bar", NULL, "baz", NULL); + wrap_json_pack(&result, "[s*,o*,O*]", NULL, NULL, NULL); + +Parsing and Validating Values +----------------------------- + +This section describes functions that help to validate complex values +and extract, or *unpack*, data from them. Like building values +<apiref-pack>, this is also based on format strings. + +While a JSON value is unpacked, the type specified in the format string +is checked to match that of the JSON value. This is the validation part +of the process. In addition to this, the unpacking functions can also +check that all items of arrays and objects are unpacked. This check be +enabled with the format specifier `!` or by using the flag +`JSON_STRICT`. See below for details. + +Here's the full list of format specifiers. The type in parentheses +denotes the JSON type, and the type in brackets (if any) denotes the C +type whose address should be passed. + +`s` (string) \[const char \*\] + +: Convert a JSON string to a pointer to a null terminated UTF-8 + string. The resulting string is extracted by using + json\_string\_value() internally, so it exists as long as there are + still references to the corresponding JSON string. + +`s%` (string) \[const char \*, size\_t \*\] + +: Convert a JSON string to a pointer to a null terminated UTF-8 string + and its length. + +`n` (null) + +: Expect a JSON null value. Nothing is extracted. + +`b` (boolean) \[int\] + +: Convert a JSON boolean value to a C int, so that `true` is converted + to 1 and `false` to 0. + +`i` (integer) \[int\] + +: Convert a JSON integer to C int. + +`I` (integer) \[json\_int\_t\] + +: Convert a JSON integer to C json\_int\_t. + +`f` (real) \[double\] + +: Convert a JSON real to C double. + +`F` (integer or real) \[double\] + +: Convert a JSON number (integer or real) to C double. + +`o` (any value) \[json\_t \*\] + +: Store a JSON value with no conversion to a json\_t pointer. + +`O` (any value) \[json\_t \*\] + +: Like `O`, but the JSON value's reference count is incremented. + +`[fmt]` (array) + +: Convert each item in the JSON array according to the inner format + string. `fmt` may contain objects and arrays, i.e. recursive value + extraction is supported. + +`{fmt}` (object) + +: Convert each item in the JSON object according to the inner format + string `fmt`. The first, third, etc. format specifier represent a + key, and must be `s`. The corresponding argument to unpack functions + is read as the object key. The second fourth, etc. format specifier + represent a value and is written to the address given as the + corresponding argument. **Note** that every other argument is read + from and every other is written to. + + `fmt` may contain objects and arrays as values, i.e. recursive value + extraction is supported. + +`!` + +: This special format specifier is used to enable the check that all + object and array items are accessed, on a per-value basis. It must + appear inside an array or object as the last format specifier before + the closing bracket or brace. + +`*` + +: This special format specifier is the opposite of `!`. This is the default. + It must appear inside an array or object as the last format specifier + before the closing bracket or brace. + +Whitespace, `:` and `,` are ignored. + +Examples: + + /* root is the JSON integer 42 */ + int myint; + wrap_json_unpack(root, "i", &myint); + assert(myint == 42); + + /* root is the JSON object {"foo": "bar", "quux": true} */ + const char *str; + int boolean; + wrap_json_unpack(root, "{s:s, s:b}", "foo", &str, "quux", &boolean); + assert(strcmp(str, "bar") == 0 && boolean == 1); + + /* root is the JSON array [[1, 2], {"baz": null} */ + wrap_json_check(root, "[[i,i], {s:n}]", "baz"); + /* returns 0 for validation success, nothing is extracted */ + + /* root is the JSON array [1, 2, 3, 4, 5] */ + int myint1, myint2; + wrap_json_unpack(root, "[ii!]", &myint1, &myint2); + /* returns -1 for failed validation */ + + /* root is an empty JSON object */ + int myint = 0, myint2 = 0, myint3 = 0; + wrap_json_unpack(root, "{s?i, s?[ii]}", + "foo", &myint1, + "bar", &myint2, &myint3); + /* myint1, myint2 or myint3 is no touched as "foo" and "bar" don't exist */ + + +Copyright +--------- + +Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/PolicyCtl-afb/CMakeLists.txt b/Controler-afb/CMakeLists.txt index d9776df..afebdf2 100644 --- a/PolicyCtl-afb/CMakeLists.txt +++ b/Controler-afb/CMakeLists.txt @@ -30,26 +30,28 @@ macro(SET_TARGET_GENSKEL TARGET_NAME API_DEF_NAME) endmacro(SET_TARGET_GENSKEL) # Add target to project dependency list -PROJECT_TARGET_ADD(polctl-afb) +PROJECT_TARGET_ADD(control-afb) # Define project Targets - ADD_LIBRARY(${TARGET_NAME} MODULE polctl-binding.c ) + ADD_LIBRARY(${TARGET_NAME} MODULE ctl-binding.c ctl-events.c ctl-policy.c ctl-lua.c +) # Generate API-v2 hat from OpenAPI json definition - SET_TARGET_GENSKEL(${TARGET_NAME} polctl-apidef) + 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 polctl + OUTPUT_NAME ${TARGET_NAME} ) # Library dependencies (include updates automatically) TARGET_LINK_LIBRARIES(${TARGET_NAME} - audio-interface + audio-common + ${link_libraries} ) # installation directory diff --git a/Controler-afb/ctl-apidef.h b/Controler-afb/ctl-apidef.h new file mode 100644 index 0000000..0ab4cd2 --- /dev/null +++ b/Controler-afb/ctl-apidef.h @@ -0,0 +1,128 @@ + +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\":\"p" + "olctl\",\"version\":\"1.0\",\"x-binding-c-generator\":{\"api\":\"control" + "\",\"version\":2,\"prefix\":\"ctlapi_\",\"postfix\":\"\",\"start\":null," + "\"onevent\":null,\"init\":\"CtlBindingInit\",\"scope\":\"\",\"private\":" + "false}},\"servers\":[{\"url\":\"ws://{host}:{port}/api/polctl\",\"descri" + "ption\":\"Unicens2 API.\",\"variables\":{\"host\":{\"default\":\"localho" + "st\"},\"port\":{\"default\":\"1234\"}},\"x-afb-events\":[{\"$ref\":\"#/c" + "omponents/schemas/afb-event\"}]}],\"components\":{\"schemas\":{\"afb-rep" + "ly\":{\"$ref\":\"#/components/schemas/afb-reply-v2\"},\"afb-event\":{\"$" + "ref\":\"#/components/schemas/afb-event-v2\"},\"afb-reply-v2\":{\"title\"" + ":\"Generic response.\",\"type\":\"object\",\"required\":[\"jtype\",\"req" + "uest\"],\"properties\":{\"jtype\":{\"type\":\"string\",\"const\":\"afb-r" + "eply\"},\"request\":{\"type\":\"object\",\"required\":[\"status\"],\"pro" + "perties\":{\"status\":{\"type\":\"string\"},\"info\":{\"type\":\"string\"" + "},\"token\":{\"type\":\"string\"},\"uuid\":{\"type\":\"string\"},\"reqid" + "\":{\"type\":\"string\"}}},\"response\":{\"type\":\"object\"}}},\"afb-ev" + "ent-v2\":{\"type\":\"object\",\"required\":[\"jtype\",\"event\"],\"prope" + "rties\":{\"jtype\":{\"type\":\"string\",\"const\":\"afb-event\"},\"event" + "\":{\"type\":\"string\"},\"data\":{\"type\":\"object\"}}}},\"x-permissio" + "ns\":{\"monitor\":{\"permission\":\"urn:AGL:permission:audio:public:moni" + "tor\"},\"multimedia\":{\"permission\":\"urn:AGL:permission:audio:public:" + "monitor\"},\"navigation\":{\"permission\":\"urn:AGL:permission:audio:pub" + "lic: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\"},\"para" + "meters\":[{\"in\":\"query\",\"name\":\"event_patern\",\"required\":true," + "\"schema\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/" + "components/responses/200\"}}}},\"/event_test\":{\"description\":\"Pause " + "Resume Test\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-perm" + "issions/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\"}}}}," + "\"/navigation\":{\"description\":\"Request Access to Navigation Audio Ch" + "annel.\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissio" + "ns/navigation\"},\"parameters\":[{\"in\":\"query\",\"name\":\"zone\",\"r" + "equired\":false,\"schema\":{\"type\":\"string\"}}],\"responses\":{\"200\"" + ":{\"$ref\":\"#/components/responses/200\"}}}},\"/lua_docall\":{\"descrip" + "tion\":\"Execute LUA string script.\",\"get\":{\"x-permissions\":{\"$ref" + "\":\"#/components/x-permissions/navigation\"},\"parameters\":[{\"in\":\"" + "query\",\"name\":\"func\",\"required\":true,\"schema\":{\"type\":\"strin" + "g\"}},{\"in\":\"query\",\"name\":\"args\",\"required\":false,\"schema\":" + "{\"type\":\"array\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/r" + "esponses/200\"}}}},\"/lua_dostring\":{\"description\":\"Execute LUA stri" + "ng script.\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permi" + "ssions/navigation\"},\"parameters\":[{\"in\":\"query\",\"required\":true" + ",\"schema\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#" + "/components/responses/200\"}}}},\"/lua_doscript\":{\"description\":\"Exe" + "cute LUA string script.\",\"get\":{\"x-permissions\":{\"$ref\":\"#/compo" + "nents/x-permissions/navigation\"},\"parameters\":[{\"in\":\"query\",\"na" + "me\":\"filename\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"" + "responses\":{\"200\":{\"$ref\":\"#/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_event_test(struct afb_req req); + void ctlapi_navigation(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 = "event_test", + .callback = ctlapi_event_test, + .auth = &_afb_auths_v2_control[0], + .info = NULL, + .session = AFB_SESSION_NONE_V2 + }, + { + .verb = "navigation", + .callback = ctlapi_navigation, + .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 = NULL, + .noconcurrency = 0 +}; + diff --git a/Controler-afb/ctl-apidef.json b/Controler-afb/ctl-apidef.json new file mode 100644 index 0000000..fddf301 --- /dev/null +++ b/Controler-afb/ctl-apidef.json @@ -0,0 +1,281 @@ +{ + "openapi": "3.0.0", + "$schema": "http:iot.bzh/download/openapi/schema-3.0/default-schema.json", + "info": { + "description": "", + "title": "polctl", + "version": "1.0", + "x-binding-c-generator": { + "api": "control", + "version": 2, + "prefix": "ctlapi_", + "postfix": "", + "start": null, + "onevent": null, + "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" + } + } + } + }, + "/event_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" + } + } + } + }, + "/navigation": { + "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 new file mode 100644 index 0000000..45c4e1c --- /dev/null +++ b/Controler-afb/ctl-binding.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author Fulup Ar Foll <fulup@iot.bzh> + * + * 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 <stdio.h> +#include <string.h> +#include <time.h> + +#include "audio-common.h" +#include "ctl-binding.h" + + + +// Include Binding Stub generated from Json OpenAPI +#include "ctl-apidef.h" + + +PUBLIC void ctlapi_navigation (afb_req request) { + + ctlapi_authorize (CTLAPI_NAVIGATION, request); +} + +PUBLIC void ctlapi_multimedia (afb_req request) { + + ctlapi_authorize (CTLAPI_MULTIMEDIA, request); +} + +PUBLIC void ctlapi_emergency (afb_req request) { + + ctlapi_authorize (CTLAPI_EMERGENCY, request); +} + +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 += PolicyInit(); + errcount += LuaLibInit(); + + 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 new file mode 100644 index 0000000..e53d2ca --- /dev/null +++ b/Controler-afb/ctl-binding.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author Fulup Ar Foll <fulup@iot.bzh> + * + * 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. + */ +#include <systemd/sd-event.h> + +#include "audio-common.h" + +#ifndef CONTROLER_BINDING_INCLUDE +#define CONTROLER_BINDING_INCLUDE + +// polctl-binding.c +PUBLIC int CtlBindingInit (); + +// ctl-timerevt.c +// ---------------------- +#define DEFAULT_PAUSE_DELAY 3000 +#define DEFAULT_TEST_COUNT 1 +typedef int (*timerCallbackT)(void *context); +typedef struct { + int value; + const char *label; +} AutoTestCtxT; + +typedef struct TimerHandleS { + int count; + int delay; + AutoTestCtxT *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); + +// ctl-policy +// ----------- + +typedef struct PolicyActionS{ + const char* label; + const char* api; + const char* verb; + json_object *argsJ; + const char *info; + int timeout; + json_object* (*actionCB)(struct PolicyActionS *action,json_object *response, void *context); +} PolicyActionT; + +typedef struct { + const char* label; + const char *info; + const char *version; + void *context; +} PolicyHandleT; + +typedef struct { + char *sharelib; + void *dlHandle; + PolicyHandleT **handle; + PolicyActionT **onload; + PolicyActionT **events; + PolicyActionT **controls; +} PolicyCtlConfigT; + +typedef enum { + CTLAPI_NAVIGATION, + CTLAPI_MULTIMEDIA, + CTLAPI_EMERGENCY, + + CTL_NONE=-1 +} PolicyCtlEnumT; + +PUBLIC int PolicyInit(void); +PUBLIC json_object* ScanForConfig (char* searchPath, char * pre, char *ext); +PUBLIC void ctlapi_authorize (PolicyCtlEnumT control, afb_req request); + +// ctl-lua.c +PUBLIC int LuaLibInit (); +PUBLIC void ctlapi_lua_docall (afb_req request); +PUBLIC void ctlapi_lua_dostring (afb_req request); +PUBLIC void ctlapi_lua_doscript (afb_req request); + +#endif diff --git a/PolicyCtl-afb/polctl-binding.c b/Controler-afb/ctl-events.c index d24b036..f444848 100644 --- a/PolicyCtl-afb/polctl-binding.c +++ b/Controler-afb/ctl-events.c @@ -21,35 +21,10 @@ #include <time.h> #include <systemd/sd-event.h> -#include "audio-interface.h" - -STATIC int polctl_init (); - -// Include Binding Stub generated from Json OpenAPI -#include "polctl-apidef.h" - -#define DEFAULT_PAUSE_DELAY 3000 -#define DEFAULT_TEST_COUNT 1 - -typedef int (*timerCallbackT)(void *context); - - -typedef struct { - int value; - const char *label; -} AutoTestCtxT; - -typedef struct TimerHandleS { - int count; - int delay; - AutoTestCtxT *context; - timerCallbackT callback; - sd_event_source *evtSource; -} TimerHandleT; +#include "ctl-binding.h" static afb_event afbevt; - STATIC int TimerNext (sd_event_source* source, uint64_t timer, void* handle) { TimerHandleT *timerHandle = (TimerHandleT*) handle; int done; @@ -71,23 +46,12 @@ STATIC int TimerNext (sd_event_source* source, uint64_t timer, void* handle) { return 0; OnErrorExit: - AFB_WARNING("TimerNext Fail tag=%s", timerHandle->context->label); + AFB_WARNING("TimerNext Callback Fail Tag=%s", timerHandle->context->label); return -1; } -STATIC void TimerStart(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, 250, TimerNext, timerHandle); -} -STATIC int DoPauseResumeCB (void *context) { +STATIC int DoSendEvent (void *context) { AutoTestCtxT *ctx= (AutoTestCtxT*)context; json_object *ctlEventJ; @@ -99,18 +63,41 @@ STATIC int DoPauseResumeCB (void *context) { json_object_object_add(ctlEventJ,"value" , json_object_new_int(ctx->value)); int done = afb_event_push(afbevt, ctlEventJ); - AFB_NOTICE ("DoPauseResumeCB {action: '%s', value:%d} status=%d", ctx->label, ctx->value, done); + AFB_NOTICE ("DoSendEvent {action: '%s', value:%d} status=%d", ctx->label, ctx->value, done); return (done); } -PUBLIC void polctl_event_test (afb_req request) { +STATIC void TimerEvtStart(TimerHandleT *timerHandle, void *context) { + uint64_t usec; + + // populate CB handle + timerHandle->callback=DoSendEvent; + 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, 250, TimerNext, timerHandle); +} + +PUBLIC afb_event TimerEvtGet(void) { + return afbevt; +} + + +// Generated some fake event based on watchdog/counter +PUBLIC void ctlapi_event_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"); @@ -127,7 +114,7 @@ PUBLIC void polctl_event_test (afb_req request) { if (timerHandle->count == 0) timerHandle->count=DEFAULT_TEST_COUNT; // start a lool timer - TimerStart (timerHandle, DoPauseResumeCB, context); + TimerEvtStart (timerHandle, context); afb_req_success(request, NULL, NULL); return; @@ -136,39 +123,17 @@ PUBLIC void polctl_event_test (afb_req request) { return; } - -PUBLIC void polctl_navigation (afb_req request) { - - AFB_NOTICE ("Enter polctl_navigation"); - afb_req_success(request, NULL, NULL); -} - -PUBLIC void polctl_monitor (afb_req request) { - - // subscribe Client to event - int err = afb_req_subscribe(request, afbevt); - 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 polctl_init () { - AFB_DEBUG ("Audio Policy Control Binding Init"); +PUBLIC int TimerEvtInit () { // create binder event to send test pause/resume afbevt = afb_daemon_make_event("request"); if (!afb_event_is_valid(afbevt)) { - AFB_ERROR ("POLCTL_INIT: Cannot register polctl event"); - return (1); + AFB_ERROR ("POLCTL_INIT: Cannot register ctl-events"); + return 1; } + AFB_DEBUG ("Audio Control-Events Init Done"); return 0; } diff --git a/Controler-afb/ctl-lua.c b/Controler-afb/ctl-lua.c new file mode 100644 index 0000000..0e44bc9 --- /dev/null +++ b/Controler-afb/ctl-lua.c @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author Fulup Ar Foll <fulup@iot.bzh> + * + * 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/ + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <dirent.h> + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + +#include "ctl-binding.h" +#include "wrap-json.h" + + +static lua_State* luaState; + +typedef enum { + LUA_DOCALL, + LUA_DOSTRING, + LUA_DOSCRIPT, +} LuaDoActionT; + +// List Avaliable Configuration Files +PUBLIC json_object* ScanForConfig (char* searchPath, char *pre, char *ext) { + json_object *responseJ; + DIR *dirHandle; + char *dirPath; + char* dirList= strdup(searchPath); + size_t extLen = strlen(ext); + + 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) { + + // check prefix and extention + size_t extIdx=strlen(dirEnt->d_name) - extLen; + if (extIdx <= 0) continue; + if (pre && !strcasestr (dirEnt->d_name, pre)) continue; + if (ext && strcasecmp (ext, &dirEnt->d_name[extIdx])) continue; + + 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); +} + +STATIC int LuaPrintNotice(lua_State* luaState) { + int count = lua_gettop(luaState); + + for (int idx=1; idx <= count; ++idx) { + const char *str = lua_tostring(luaState, idx); // Get string + + // Output string. + AFB_NOTICE (str); + } + + return 0; // no value return +} + +STATIC int LuaPushArgument (json_object *arg) { + + switch (json_object_get_type(arg)) { + case json_type_object: + lua_newtable (luaState); + json_object_object_foreach (arg, key, val) { + int done = LuaPushArgument (val); + if (done) { + lua_pushstring(luaState, key); // table.key = val + lua_settable(luaState, -3); + } + } + + break; + case json_type_int: + lua_pushinteger(luaState, json_object_get_int(arg)); + break; + case json_type_string: + lua_pushstring(luaState, json_object_get_string(arg)); + break; + case json_type_boolean: + lua_pushboolean(luaState, json_object_get_boolean(arg)); + break; + case json_type_double: + lua_pushnumber(luaState, json_object_get_double(arg)); + break; + default: + return 0; + } + + return 1; +} + +// Generated some fake event based on watchdog/counter +PUBLIC 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; + } + break; + } + + case LUA_DOCALL: { + const char *func; + json_object *args; + err= wrap_json_unpack (queryJ, "{s:s, s?o !}", "func", &func,"args", &args); + if (err) { + AFB_ERROR ("LUA-DOCALL-SYNTAX missing func|args args=%s", json_object_get_string(queryJ)); + goto OnErrorExit; + } + + // load function (should exist in CONTROL_PATH_LUA + lua_getglobal(luaState, func); + + // push arguments on the stack + if (json_object_get_type(args) != json_type_array) { + count= LuaPushArgument (args); + } else { + for (int idx=0; idx<json_object_array_length(args); idx++) { + count += LuaPushArgument (json_object_array_get_idx(args, idx)); + if (err) break; + } + } + + break; + } + + case LUA_DOSCRIPT: { + const char *script; + json_object *args; + int index; + + // scan luascript search path once + static json_object *luaScriptPathJ =NULL; + if (!luaScriptPathJ) luaScriptPathJ= ScanForConfig(CONTROL_PATH_LUA , NULL, "lua"); + + err= wrap_json_unpack (queryJ, "{s:s, s?o s?o !}", "script", &script,"args", &args, "arg", &args); + if (err) { + AFB_ERROR ("LUA-DOCALL-SYNTAX missing script|(args,arg) args=%s", json_object_get_string(queryJ)); + goto OnErrorExit; + } + + // push arguments on the stack + if (json_object_get_type(args) != json_type_array) { + lua_createtable(luaState, 1, 0); + count= LuaPushArgument (args); + } else { + int length= json_object_array_length(args); + lua_createtable(luaState, length, 0); + for (int idx=0; idx < length; idx++) { + count += LuaPushArgument (json_object_array_get_idx(args, idx)); + if (err) break; + } + } + + for (index=0; index < json_object_array_length(luaScriptPathJ); index++) { + char *filename; char*dirpath; + json_object *entryJ=json_object_array_get_idx(luaScriptPathJ, index); + + err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "dirpath", &dirpath,"filename", &filename); + if (err) { + AFB_ERROR ("LUA-DOSCRIPT HOOPs invalid LUA script path = %s", json_object_get_string(entryJ)); + goto OnErrorExit; + } + + if (strcmp(filename, script)) continue; + + char filepath[255]; + strncpy(filepath, dirpath, sizeof(filepath)); + strncat(filepath, "/", sizeof(filepath)); + strncat(filepath, filename, sizeof(filepath)); + err= luaL_loadfile(luaState, filepath); + if (err) { + AFB_ERROR ("LUA-DOSCRIPT HOOPs Error in LUA loading scripts=%s err=%s", filepath, lua_tostring(luaState,-1)); + goto OnErrorExit; + } + break; + } + + if (index == json_object_array_length(luaScriptPathJ)) { + AFB_ERROR ("LUA-DOSCRIPT HOOPs script=%s not in path=%s", script, CONTROL_PATH_LUA); + goto OnErrorExit; + + } + + //lua_setglobal(luaState, "args"); + count=1; + + break; + } + + default: + AFB_ERROR ("LUA-DOCALL-ACTION unknown query=%s", json_object_get_string(queryJ)); + goto OnErrorExit; + } + + // effectively exec LUA code +// err=lua_pcall(luaState, count, LUA_MULTRET, 0); + err=lua_pcall(luaState, count, LUA_MULTRET, 0); + if (err) { + AFB_ERROR ("LUA-DO-EXEC:FAIL query=%s err=%s", json_object_get_string(queryJ), lua_tostring(luaState,-1) ); + goto OnErrorExit; + } + + // number of return values + count=lua_gettop(luaState); + + // Check status return true=OK false=fail + if (count == 0 || !lua_isboolean(luaState, 1)) { + AFB_ERROR ("LUA-DO-RETURN:INVALID 1st return argument not boolean query=%s", json_object_get_string(queryJ) ); + goto OnErrorExit; + } + + // get return fail/ok status + const int returnStatus=lua_toboolean(luaState, 1); + + // loop on remaining return arguments + json_object *returnArgs = json_object_new_array(); + for (int idx=2; idx <= count; idx++) { + + // push on stack return value (Fulup in LUA-5.3 the two functions are combined) + int valueType=lua_type (luaState, idx); + + switch(valueType) { + case LUA_TNUMBER: + json_object_array_add(returnArgs, json_object_new_double(lua_tonumber(luaState, idx))); + break; + case LUA_TBOOLEAN: + json_object_array_add(returnArgs, json_object_new_boolean(lua_toboolean(luaState, idx))); + break; + case LUA_TSTRING: + json_object_array_add(returnArgs, json_object_new_string(lua_tostring(luaState, idx))); + break; + case LUA_TTABLE: + default: + AFB_NOTICE ("script returned Unknown/Unsupported type: "); + } + + } + + // pop stack including Func/String/Script call + lua_pop(luaState, count+1); + + if(returnStatus) + afb_req_success(request, returnArgs, NULL); + else + afb_req_fail(request, "LUA-DOACTION-FAIL", json_object_get_string(returnArgs)); + + 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); +} + + +// Create Binding Event at Init +PUBLIC int LuaLibInit () { + int err, index; + + // search for default policy config file + json_object *luaScriptPathJ = ScanForConfig(CONTROL_PATH_LUA , "onload", "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 + lua_register(luaState,"print", LuaPrintNotice ); + //lua_register(lauHandle,"AFB_DEBUG", LuaAFBDebug); + + // 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*dirpath; + err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "dirpath", &dirpath,"filename", &filename); + if (err) { + AFB_ERROR ("LUA-INIT HOOPs invalid config file path = %s", json_object_get_string(entryJ)); + goto OnErrorExit; + } + + char filepath[255]; + strncpy(filepath, dirpath, 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_PATH_LUA); + } + + + AFB_DEBUG ("Audio control-LUA Init Done"); + return 0; + + OnErrorExit: + return -1; +} +
\ No newline at end of file diff --git a/Controler-afb/ctl-policy.c b/Controler-afb/ctl-policy.c new file mode 100644 index 0000000..7ad73fa --- /dev/null +++ b/Controler-afb/ctl-policy.c @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author Fulup Ar Foll <fulup@iot.bzh> + * + * 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 <stdio.h> +#include <string.h> + +#include <json-c/json_object.h> + +#include "wrap-json.h" +#include "ctl-binding.h" + +STATIC PolicyCtlConfigT *ctlHandle = NULL; + + +PUBLIC void ctlapi_authorize (PolicyCtlEnumT control, afb_req request) { + json_object*tmpJ; + + json_object* argsJ= afb_req_json(request); + int done=json_object_object_get_ex(argsJ, "closing", &tmpJ); + if (done) return; + +} + + +// List Avaliable Configuration Files +PUBLIC void ctlapi_config (struct afb_req request) { + json_object*tmpJ; + char *dirList; + + json_object* argsJ = afb_req_json(request); + if (argsJ && json_object_object_get_ex (argsJ, "cfgpath" , &tmpJ)) { + dirList = strdup (json_object_get_string(tmpJ)); + } else { + dirList = strdup (CONTROL_PATH_POLICY); + AFB_NOTICE ("CONFIG-MISSING: use default CONTROL_PATH_POLICY=%s", CONTROL_PATH_POLICY); + } + + // get list of config file + struct json_object *responseJ = ScanForConfig(dirList, "onload", "json"); + + if (json_object_array_length(responseJ) == 0) { + afb_req_fail(request, "CONFIGPATH:EMPTY", "No Config Found in CONTROL_PATH_POLICY"); + } else { + afb_req_success(request, responseJ, NULL); + } + + return; +} + +STATIC PolicyActionT *PolicyLoadActions (json_object *actionsJ) { + int err; + PolicyActionT *actions; + char *callback; // need to search in DL lib + + // unpack individual action object + int actionUnpack (json_object *actionJ, PolicyActionT *action) { + + err= wrap_json_unpack(actionJ, "{ss,s?s,s?o,s?s,s?s,s?s !}" + , "label",&action->label, "info",&action->info, "callback",&callback, "args",&action->argsJ, "api",&action->api, "verb", &action->verb); + if (err) { + AFB_ERROR ("POLICY-LOAD-ACTION Missing something label|info|callback|api|verb|args in %s", json_object_get_string(actionJ)); + return -1; + } + if (!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 policyConfig; + +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_PATH_POLICY, "onload", "json"); + return 0; + + 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_FILE_POLICY)) { + char filepath[255]; + strncpy(filepath, dirpath, sizeof(filepath)); + strncat(filepath, "/", sizeof(filepath)); + strncat(filepath, filename, sizeof(filepath)); + ctlHandle = PolicyLoadConfig (filepath); + if (!ctlHandle) { + AFB_ERROR ("POLICY-INIT:ERROR No File to load [%s]", filepath); + goto OnErrorExit; + } + break; + } + } + + // no policy config found remove control API from binder + if (index == 0) { + AFB_WARNING ("POLICY-INIT:WARNING No Control policy file [%s]", CONTROL_FILE_POLICY); + } + + AFB_NOTICE ("POLICY-INIT:SUCCES: Audio Control Policy Init"); + return 0; + +OnErrorExit: + AFB_NOTICE ("POLICY-INIT:ERROR: Audio Control Policy Init"); + return 1; +} + + + diff --git a/HAL-afb/HAL-interface/CMakeLists.txt b/HAL-afb/HAL-interface/CMakeLists.txt index d20a283..37d82de 100644 --- a/HAL-afb/HAL-interface/CMakeLists.txt +++ b/HAL-afb/HAL-interface/CMakeLists.txt @@ -28,7 +28,7 @@ PROJECT_TARGET_ADD(hal-interface) # Library dependencies (include updates automatically) TARGET_LINK_LIBRARIES(${TARGET_NAME} - audio-interface + audio-common ) # Define target includes diff --git a/HAL-afb/HAL-interface/hal-interface.h b/HAL-afb/HAL-interface/hal-interface.h index a6310e7..c210f02 100644 --- a/HAL-afb/HAL-interface/hal-interface.h +++ b/HAL-afb/HAL-interface/hal-interface.h @@ -21,7 +21,7 @@ #include <stdio.h> #include <alsa/asoundlib.h> -#include "audio-interface.h" +#include "audio-common.h" #include <systemd/sd-event.h> typedef enum { diff --git a/HAL-afb/HDA-intel/CMakeLists.txt b/HAL-afb/HDA-intel/CMakeLists.txt index efe244d..37e277b 100644 --- a/HAL-afb/HDA-intel/CMakeLists.txt +++ b/HAL-afb/HDA-intel/CMakeLists.txt @@ -34,7 +34,7 @@ PROJECT_TARGET_ADD(hal-intel-hda) # Library dependencies (include updates automatically) TARGET_LINK_LIBRARIES(${TARGET_NAME} hal-interface - audio-interface + audio-common ) # installation directory diff --git a/HAL-afb/HDA-intel/IntelHdaHAL.c b/HAL-afb/HDA-intel/IntelHdaHAL.c index f5c260e..ab822ca 100644 --- a/HAL-afb/HDA-intel/IntelHdaHAL.c +++ b/HAL-afb/HDA-intel/IntelHdaHAL.c @@ -23,7 +23,7 @@ */ #define _GNU_SOURCE #include "hal-interface.h" -#include "audio-interface.h" +#include "audio-common.h" // Default Values for MasterVolume Ramping diff --git a/HAL-afb/Jabra-Solemate/CMakeLists.txt b/HAL-afb/Jabra-Solemate/CMakeLists.txt index 9504175..973e95d 100644 --- a/HAL-afb/Jabra-Solemate/CMakeLists.txt +++ b/HAL-afb/Jabra-Solemate/CMakeLists.txt @@ -34,7 +34,7 @@ PROJECT_TARGET_ADD(hal-jabra-usb) # Library dependencies (include updates automatically) TARGET_LINK_LIBRARIES(${TARGET_NAME} hal-interface - audio-interface + audio-common ) # installation directory diff --git a/HAL-afb/Jabra-Solemate/JabraUsbHAL.c b/HAL-afb/Jabra-Solemate/JabraUsbHAL.c index f3753ec..7eeab90 100644 --- a/HAL-afb/Jabra-Solemate/JabraUsbHAL.c +++ b/HAL-afb/Jabra-Solemate/JabraUsbHAL.c @@ -24,7 +24,7 @@ */ #define _GNU_SOURCE #include "hal-interface.h" -#include "audio-interface.h" +#include "audio-common.h" // Define few private tag for not standard functions #define PCM_Volume_Multimedia 1000 diff --git a/HAL-afb/Scarlett-Focusrite/CMakeLists.txt b/HAL-afb/Scarlett-Focusrite/CMakeLists.txt index 93752a7..48d5964 100644 --- a/HAL-afb/Scarlett-Focusrite/CMakeLists.txt +++ b/HAL-afb/Scarlett-Focusrite/CMakeLists.txt @@ -34,7 +34,7 @@ PROJECT_TARGET_ADD(hal-scalett-usb) # Library dependencies (include updates automatically) TARGET_LINK_LIBRARIES(${TARGET_NAME} hal-interface - audio-interface + audio-common ) # installation directory diff --git a/HAL-afb/Scarlett-Focusrite/ScarlettUsbHAL.c b/HAL-afb/Scarlett-Focusrite/ScarlettUsbHAL.c index 9b31c96..94bfa4f 100644 --- a/HAL-afb/Scarlett-Focusrite/ScarlettUsbHAL.c +++ b/HAL-afb/Scarlett-Focusrite/ScarlettUsbHAL.c @@ -24,7 +24,7 @@ */ #define _GNU_SOURCE #include "hal-interface.h" -#include "audio-interface.h" +#include "audio-common.h" // Default Values for MasterVolume Ramping STATIC halVolRampT volRampMaster= { diff --git a/PolicyCtl-afb/polctl-apidef.h b/PolicyCtl-afb/polctl-apidef.h deleted file mode 100644 index 9e2b1a7..0000000 --- a/PolicyCtl-afb/polctl-apidef.h +++ /dev/null @@ -1,90 +0,0 @@ - -static const char _afb_description_v2_polctl[] = - "{\"openapi\":\"3.0.0\",\"$schema\":\"http:iot.bzh/download/openapi/schem" - "a-3.0/default-schema.json\",\"info\":{\"description\":\"\",\"title\":\"p" - "olctl\",\"version\":\"1.0\",\"x-binding-c-generator\":{\"api\":\"polctl\"" - ",\"version\":2,\"prefix\":\"polctl_\",\"postfix\":\"\",\"start\":null,\"" - "onevent\":null,\"init\":\"polctl_init\",\"scope\":\"\",\"private\":false" - "}},\"servers\":[{\"url\":\"ws://{host}:{port}/api/polctl\",\"description" - "\":\"Unicens2 API.\",\"variables\":{\"host\":{\"default\":\"localhost\"}" - ",\"port\":{\"default\":\"1234\"}},\"x-afb-events\":[{\"$ref\":\"#/compon" - "ents/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\":\"Ge" - "neric response.\",\"type\":\"object\",\"required\":[\"jtype\",\"request\"" - "],\"properties\":{\"jtype\":{\"type\":\"string\",\"const\":\"afb-reply\"" - "},\"request\":{\"type\":\"object\",\"required\":[\"status\"],\"propertie" - "s\":{\"status\":{\"type\":\"string\"},\"info\":{\"type\":\"string\"},\"t" - "oken\":{\"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\":{\"t" - "ype\":\"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:monit" - "or\"},\"emergency\":{\"permission\":\"urn:AGL:permission:audio:public:em" - "ergency\"}},\"responses\":{\"200\":{\"description\":\"A complex object a" - "rray response\",\"content\":{\"application/json\":{\"schema\":{\"$ref\":" - "\"#/components/schemas/afb-reply\"}}}}}},\"paths\":{\"/monitor\":{\"desc" - "ription\":\"Subcribe Audio Agent Policy Control End\",\"get\":{\"x-permi" - "ssions\":{\"$ref\":\"#/components/x-permissions/monitor\"},\"parameters\"" - ":[{\"in\":\"query\",\"name\":\"event_patern\",\"required\":true,\"schema" - "\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/componen" - "ts/responses/200\"}}}},\"/event_test\":{\"description\":\"Pause Resume T" - "est\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions/" - "monitor\"},\"parameters\":[{\"in\":\"query\",\"name\":\"delay\",\"requir" - "ed\":false,\"schema\":{\"type\":\"interger\"}},{\"in\":\"query\",\"name\"" - ":\"count\",\"required\":false,\"schema\":{\"type\":\"interger\"}}],\"res" - "ponses\":{\"200\":{\"$ref\":\"#/components/responses/200\"}}}},\"/naviga" - "tion\":{\"description\":\"Request Access to Navigation Audio Channel.\"," - "\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions/naviga" - "tion\"},\"parameters\":[{\"in\":\"query\",\"name\":\"zone\",\"required\"" - ":false,\"schema\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref" - "\":\"#/components/responses/200\"}}}}}}" -; - -static const struct afb_auth _afb_auths_v2_polctl[] = { - { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:monitor" } -}; - - void polctl_monitor(struct afb_req req); - void polctl_event_test(struct afb_req req); - void polctl_navigation(struct afb_req req); - -static const struct afb_verb_v2 _afb_verbs_v2_polctl[] = { - { - .verb = "monitor", - .callback = polctl_monitor, - .auth = &_afb_auths_v2_polctl[0], - .info = NULL, - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "event_test", - .callback = polctl_event_test, - .auth = &_afb_auths_v2_polctl[0], - .info = NULL, - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "navigation", - .callback = polctl_navigation, - .auth = &_afb_auths_v2_polctl[0], - .info = NULL, - .session = AFB_SESSION_NONE_V2 - }, - { .verb = NULL } -}; - -const struct afb_binding_v2 afbBindingV2 = { - .api = "polctl", - .specification = _afb_description_v2_polctl, - .info = NULL, - .verbs = _afb_verbs_v2_polctl, - .preinit = NULL, - .init = polctl_init, - .onevent = NULL, - .noconcurrency = 0 -}; - diff --git a/PolicyCtl-afb/polctl-apidef.json b/PolicyCtl-afb/polctl-apidef.json deleted file mode 100644 index 65fe0ec..0000000 --- a/PolicyCtl-afb/polctl-apidef.json +++ /dev/null @@ -1,175 +0,0 @@ -{ - "openapi": "3.0.0", - "$schema": "http:iot.bzh/download/openapi/schema-3.0/default-schema.json", - "info": { - "description": "", - "title": "polctl", - "version": "1.0", - "x-binding-c-generator": { - "api": "polctl", - "version": 2, - "prefix": "polctl_", - "postfix": "", - "start": null , - "onevent": null, - "init": "polctl_init", - "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"} - } - } - }, - "/event_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"} - } - } - }, - "/navigation": { - "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"} - } - } - } - } -} diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake index 1a53d8a..6fad621 100644 --- a/conf.d/cmake/config.cmake +++ b/conf.d/cmake/config.cmake @@ -62,6 +62,7 @@ set (PKG_REQUIRED_LIST afb-daemon json-c libafbwsc + lua>=5.3 ) # LANG Specific compile flags set for all build types @@ -71,20 +72,23 @@ set (PKG_REQUIRED_LIST # Do not optimise when debugging set(CMAKE_C_FLAGS_DEBUG "-g -ggdb -Wp,-U_FORTIFY_SOURCE") +# (BUG!!!) as PKG_CONFIG_PATH does not work [should be an env variable] +# --------------------------------------------------------------------- +set(CMAKE_INSTALL_PREFIX $ENV{HOME}/opt) +set(CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX}/lib64/pkgconfig ${CMAKE_INSTALL_PREFIX}/lib/pkgconfig) +set(LD_LIBRARY_PATH ${CMAKE_INSTALL_PREFIX}/lib64 ${CMAKE_INSTALL_PREFIX}/lib) + # Define CONTROL_CDEV_NAME should match MOST driver values # --------------------------------------------------------- - add_compile_options(-DCONTROL_CDEV_TX="/dev/inic-usb-ctx") - add_compile_options(-DCONTROL_CDEV_RX="/dev/inic-usb-crx") + add_compile_options(-DCONTROL_FILE_POLICY="onload-control-policy.json") + add_compile_options(-DCONTROL_PATH_POLICY="/etc/default/audio-agent/policy:$ENV{HOME}/.config/audio-agent:${CMAKE_INSTALL_PREFIX}/audio-agent/policy:${CMAKE_SOURCE_DIR}/data") + add_compile_options(-DCONTROL_PATH_LUA="/etc/default/audio-agent/policy:$ENV{HOME}/.config/audio-agent:${CMAKE_INSTALL_PREFIX}/audio-agent/policy:${CMAKE_SOURCE_DIR}/data") + # Print a helper message when every thing is finished # ---------------------------------------------------- set(CLOSING_MESSAGE "Debug in ./buid: afb-daemon --port=1234 --ldpaths=. --workdir=. --roothttp=../htdocs --tracereq=common --token='' --verbose") -# (BUG!!!) as PKG_CONFIG_PATH does not work [should be an env variable] -# --------------------------------------------------------------------- -set(CMAKE_INSTALL_PREFIX $ENV{HOME}/opt) -set(CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX}/lib64/pkgconfig ${CMAKE_INSTALL_PREFIX}/lib/pkgconfig) -set(LD_LIBRARY_PATH ${CMAKE_INSTALL_PREFIX}/lib64 ${CMAKE_INSTALL_PREFIX}/lib) # Optional dependencies order # --------------------------- diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt new file mode 100644 index 0000000..55ee664 --- /dev/null +++ b/data/CMakeLists.txt @@ -0,0 +1,46 @@ +########################################################################### +# Copyright 2017 IoT.bzh +# +# author: Fulup Ar Foll <fulup@iot.bzh> +# +# 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. +########################################################################### + + + +################################################## +# Control Policy Config file +################################################## +PROJECT_TARGET_ADD(Control_config) + + file(GLOB XML_FILES "*.json") + + add_custom_target(${TARGET_NAME} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME} + ) + + # check XML schema before pushing config + add_custom_command( + DEPENDS ${XML_FILES} + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + # COMMAND xmllint -schema ${XML_SCHEMA} ${XML_FILES} --noout (Fulup we miss this for JSON) + COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME} + COMMAND touch ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME} + COMMAND cp -r ${XML_FILES} ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME} + ) + + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + LABELS "DATA" + OUTPUT_NAME ${TARGET_NAME} + ) diff --git a/data/helloworld-lua-script.lua b/data/helloworld-lua-script.lua new file mode 100644 index 0000000..8386bc0 --- /dev/null +++ b/data/helloworld-lua-script.lua @@ -0,0 +1,41 @@ +--[[ + Copyright (C) 2016 "IoT.bzh" + Author Fulup Ar Foll <fulup@iot.bzh> + + 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. + + + Simple API script +--]] + + -- retrieve calling arguments + args= ... + + print ("Helloworld script arguments:") + + + for key,value in pairs(args) + do + print("args: ", key,value) + end + + -- loop on script arguments + --for i=1,#args + --do + -- print(" -- ", i, args[i]) + --end + + + -- return two arguments on top of status + return true, 1234, "ABCD" + diff --git a/data/onload-control-policy.json b/data/onload-control-policy.json new file mode 100644 index 0000000..d910893 --- /dev/null +++ b/data/onload-control-policy.json @@ -0,0 +1,90 @@ +{ + "$schema": "ToBeDone", + "metadata": { + "label": "sample-audio-policy", + "info": "Provide Default Audio Policy for Multimedia, Navigation and Emergency", + "version": "1.0" + }, + "onload": { + "info": "controler initialisation config", + "plugin": "sample-audio-policy.so", + "actions": [ + { + "info": "Call policy sharelib install entrypoint", + "callback": "SamplePolicyInstall", + "args": {"arg1" : "first_arg", "nextarg": "second arg value"} + }, { + "info": "Assert AlsaCore Presence", + "api": "alsacore", + "verb": "ping" + } + ] + }, + "controls": + [{ + "label": "multimedia", + "actions": [ + { + "label": "multimedia-policy-cb", + "info": "Call Sharelib Sample Callback", + "callback": "samplePolicyCB", + "args": { + "arg1": "snoopy", + "arg2": "toto" + } + }, { + "label": "multimedia-policy-ucm", + "info": "Subcall AlSA UCM navigation", + "api": "alsacore", + "verb": "ucmset", + "args": { + "verb": "multimedia" + } + } + ] + }, + { + "label":"navigation", + "action" : { + "api": "alsacore", + "verb": "ucmset", + "args": { + "verb": "navigation" + }, + "optional": true, + "timeout": 100 + } + }, { + "label":"emergency", + "action": { + "api": "alsacore", + "verb": "ucmset", + "args": { + "verb": "emergency" + } + } + }] + , + "events": [ + { + "label": "SampleEvent", + "comment": "define action when receiving a given event", + "actions": [ + { + "info": "Event Callback-1", + "callback": "ProcessEventCB", + "args": { + "arg": "action-1" + } + }, { + "info": "Event Callback-2", + "callback": "ProcessEventCB", + "args": { + "arg": "action-2" + } + } + ] + } + ] +} + diff --git a/data/onload-control-script.lua b/data/onload-control-script.lua new file mode 100644 index 0000000..7ded3be --- /dev/null +++ b/data/onload-control-script.lua @@ -0,0 +1,51 @@ +--[[ + Copyright (C) 2016 "IoT.bzh" + Author Fulup Ar Foll <fulup@iot.bzh> + + 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. + + + Provide sample policy function for AGL Advance Audio Agent +--]] + +count=0 + +-- Adjust Volume function of vehicle speed +function Adjust_Volume_Speed (speed_meters_second) + + message= string.format("**** Adjust_Volume_Speed speed=%d count=%d", speed_meters_second, count); + print (message); + + -- compute volume + volume = speed_meters_second * 2 + count=count+1 + + return true, volume, count +end + +function Ping_Test(...) + + print ("Ping_Test script arguments:"); + + for i,v in ipairs(arg) + do + print(" -- ", tostring(v)) + end + + + -- return two arguments on top of status + return true, 1234, "ABCD" + +end + + diff --git a/htdocs/audio-control.html b/htdocs/audio-control.html new file mode 100644 index 0000000..3be1ef7 --- /dev/null +++ b/htdocs/audio-control.html @@ -0,0 +1,40 @@ +<html> +<head> + <title>Basic Audio Hardware Abstraction Layer Test</title> + <link rel="stylesheet" href="AudioBinding.css"> + <script type="text/javascript" src="AFB-websock.js"></script> + <script type="text/javascript" src="AudioBinding.js"></script> +</head> + + +<body onload="init('hal_registry','alsacore', 'hallist')"> + + <h1>Simple Audio Control test</h1> + <button id="connected" onclick="init('alsa_registry','alsacore', 'getinfo');">Binder WS Fail</button> + <br><br> + + <b>API Verbosity </b> + <select id='api_verbosity' onclick='mode=this.value'> + <option value='0'>Quiet</option> + <option value='1'>Compact</option> + <option value='2'>Verbose</option> + <option value='3'>Full</option> + </select> + <br> + + <br> + <ol> + + <li><button onclick="callbinder('control','lua_dostring','print(\'Bonjours\'); return true, 4567');">LUA string</button></li> + <li><button onclick="callbinder('control','lua_docall' , {'func':'Adjust_Volume_Speed', 'args':[20]});">LUA function</button></li> + <li><button onclick="callbinder('control','lua_doscript', {'script':'helloworld-lua-script.lua', args:[{'arg1':'abcd'}, {'next':7890}, {'last':'xyz'}]});">LUA script</button></li> + + </ol> + + <div id="main" style="visibility:hidden"> + <ol> + <li>Question <pre id="question"></pre> + <li>Response <pre id="output"></pre> + <li>Events: <pre id="outevt"></pre> + </ol> + </div> diff --git a/htdocs/docs/AAAA-architecture.odg b/htdocs/docs/AAAA-architecture.odg Binary files differindex 1ebdb1c..90a2486 100644 --- a/htdocs/docs/AAAA-architecture.odg +++ b/htdocs/docs/AAAA-architecture.odg diff --git a/htdocs/docs/AAAA-architecture.pdf b/htdocs/docs/AAAA-architecture.pdf Binary files differindex a9d0605..3b6280d 100644 --- a/htdocs/docs/AAAA-architecture.pdf +++ b/htdocs/docs/AAAA-architecture.pdf diff --git a/htdocs/index.html b/htdocs/index.html index 2d2b4ae..4b842c1 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -6,4 +6,4 @@ <ol> <li><a href="alsa-core.html">AlsaCore Low Level Binding</a> <li><a href="alsa-hal.html" >AlsaHAL Hardware Abstraction Layer</a> - <li><a href="audio-logic.html">AudioLogic High level business API</a> + <li><a href="audio-control.html">AudioControl Control/Policy API</a> diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml index e955bcb..0207747 100644 --- a/nbproject/configurations.xml +++ b/nbproject/configurations.xml @@ -21,6 +21,10 @@ <in>PolicyHookCb.c</in> </df> </df> + <df name="Audio-Common"> + <in>audio-common.c</in> + <in>wrap-json.c</in> + </df> <df name="build"> <df name="CMakeFiles"> <df name="3.5.2"> @@ -38,6 +42,12 @@ <df name="Common"> <in>AudioCommonLib.c</in> </df> + <df name="Controler-afb"> + <in>ctl-binding.c</in> + <in>ctl-events.c</in> + <in>ctl-lua.c</in> + <in>ctl-policy.c</in> + </df> <df name="HAL-afb"> <df name="HAL-interface"> <in>hal-interface.c</in> @@ -83,12 +93,6 @@ <in>Mediator.cpp</in> <in>libmostvolume.cpp</in> </df> - <df name="PolicyCtl-afb"> - <in>polctl-binding.c</in> - </df> - <df name="Shared-Interface"> - <in>audio-interface.c</in> - </df> </df> <logicalFolder name="ExternalFiles" displayName="Important Files" @@ -163,10 +167,34 @@ <cTool flags="1"> </cTool> </item> + <item path="Audio-Common/audio-common.c" ex="false" tool="0" flavor2="3"> + <cTool flags="1"> + </cTool> + </item> + <item path="Audio-Common/wrap-json.c" ex="false" tool="0" flavor2="3"> + <cTool flags="1"> + </cTool> + </item> <item path="Common/AudioCommonLib.c" ex="false" tool="0" flavor2="2"> <cTool flags="0"> </cTool> </item> + <item path="Controler-afb/ctl-binding.c" ex="false" tool="0" flavor2="3"> + <cTool flags="1"> + </cTool> + </item> + <item path="Controler-afb/ctl-events.c" ex="false" tool="0" flavor2="3"> + <cTool flags="1"> + </cTool> + </item> + <item path="Controler-afb/ctl-lua.c" ex="false" tool="0" flavor2="3"> + <cTool flags="1"> + </cTool> + </item> + <item path="Controler-afb/ctl-policy.c" ex="false" tool="0" flavor2="3"> + <cTool flags="1"> + </cTool> + </item> <item path="HAL-afb/HAL-interface/hal-interface.c" ex="false" tool="0" @@ -278,14 +306,6 @@ </item> <item path="MostVolume/libmostvolume.cpp" ex="false" tool="1" flavor2="4"> </item> - <item path="PolicyCtl-afb/polctl-binding.c" ex="false" tool="0" flavor2="3"> - <cTool flags="1"> - </cTool> - </item> - <item path="Shared-Interface/audio-interface.c" ex="false" tool="0" flavor2="3"> - <cTool flags="1"> - </cTool> - </item> <folder path="0/ALSA-afb"> <cTool> <incDir> @@ -293,15 +313,15 @@ <pElem>../../../opt/include/alsa</pElem> <pElem>/usr/include/p11-kit-1</pElem> <pElem>/usr/include/json-c</pElem> - <pElem>Shared-Interface</pElem> + <pElem>Audio-Common</pElem> <pElem>build/ALSA-afb</pElem> </incDir> <preprocessorList> - <Elem>CONTROL_CDEV_RX="/dev/inic-usb-crx"</Elem> - <Elem>CONTROL_CDEV_TX="/dev/inic-usb-ctx"</Elem> + <Elem>CONTROL_FILE_POLICY="onload-control-policy.json"</Elem> + <Elem>CONTROL_PATH_LUA="/etc/default/audio-agent/policy:/home/fulup/.config/audio-agent:/home/fulup/opt/audio-agent/policy:/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/data"</Elem> + <Elem>CONTROL_PATH_POLICY="/etc/default/audio-agent/policy:/home/fulup/.config/audio-agent:/home/fulup/opt/audio-agent/policy:/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/data"</Elem> <Elem>MAX_LINEAR_DB_SCALE=24</Elem> <Elem>MAX_SND_CARD=16</Elem> - <Elem>NATIVE_LINUX</Elem> <Elem>TLV_BYTE_SIZE=256</Elem> <Elem>alsa_lowlevel_EXPORTS</Elem> </preprocessorList> @@ -317,17 +337,37 @@ <pElem>build/Alsa-Plugin/Alsa-Policy-Hook</pElem> </incDir> <preprocessorList> - <Elem>CONTROL_CDEV_RX="/dev/inic-usb-crx"</Elem> - <Elem>CONTROL_CDEV_TX="/dev/inic-usb-ctx"</Elem> + <Elem>CONTROL_FILE_POLICY="onload-control-policy.json"</Elem> + <Elem>CONTROL_PATH_LUA="/etc/default/audio-agent/policy:/home/fulup/.config/audio-agent:/home/fulup/opt/audio-agent/policy:/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/data"</Elem> + <Elem>CONTROL_PATH_POLICY="/etc/default/audio-agent/policy:/home/fulup/.config/audio-agent:/home/fulup/opt/audio-agent/policy:/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/data"</Elem> <Elem>MAX_LINEAR_DB_SCALE=24</Elem> <Elem>MAX_SND_CARD=16</Elem> - <Elem>NATIVE_LINUX</Elem> <Elem>PIC</Elem> <Elem>TLV_BYTE_SIZE=256</Elem> <Elem>policy_hook_cb_EXPORTS</Elem> </preprocessorList> </cTool> </folder> + <folder path="0/Audio-Common"> + <cTool> + <incDir> + <pElem>../../../opt/include</pElem> + <pElem>../../../opt/include/alsa</pElem> + <pElem>/usr/include/p11-kit-1</pElem> + <pElem>/usr/include/json-c</pElem> + <pElem>Audio-Common</pElem> + <pElem>build/Audio-Common</pElem> + </incDir> + <preprocessorList> + <Elem>CONTROL_FILE_POLICY="onload-control-policy.json"</Elem> + <Elem>CONTROL_PATH_LUA="/etc/default/audio-agent/policy:/home/fulup/.config/audio-agent:/home/fulup/opt/audio-agent/policy:/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/data"</Elem> + <Elem>CONTROL_PATH_POLICY="/etc/default/audio-agent/policy:/home/fulup/.config/audio-agent:/home/fulup/opt/audio-agent/policy:/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/data"</Elem> + <Elem>MAX_LINEAR_DB_SCALE=24</Elem> + <Elem>MAX_SND_CARD=16</Elem> + <Elem>TLV_BYTE_SIZE=256</Elem> + </preprocessorList> + </cTool> + </folder> <folder path="0/Common"> <cTool> <incDir> @@ -344,6 +384,28 @@ </preprocessorList> </cTool> </folder> + <folder path="0/Controler-afb"> + <cTool> + <incDir> + <pElem>../../../opt/include</pElem> + <pElem>../../../opt/include/alsa</pElem> + <pElem>/usr/include/p11-kit-1</pElem> + <pElem>/usr/include/json-c</pElem> + <pElem>/usr/include/lua5.3</pElem> + <pElem>Audio-Common</pElem> + <pElem>build/Controler-afb</pElem> + </incDir> + <preprocessorList> + <Elem>CONTROL_FILE_POLICY="onload-control-policy.json"</Elem> + <Elem>CONTROL_PATH_LUA="/etc/default/audio-agent/policy:/home/fulup/.config/audio-agent:/home/fulup/opt/audio-agent/policy:/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/data"</Elem> + <Elem>CONTROL_PATH_POLICY="/etc/default/audio-agent/policy:/home/fulup/.config/audio-agent:/home/fulup/opt/audio-agent/policy:/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/data"</Elem> + <Elem>MAX_LINEAR_DB_SCALE=24</Elem> + <Elem>MAX_SND_CARD=16</Elem> + <Elem>TLV_BYTE_SIZE=256</Elem> + <Elem>control_afb_EXPORTS</Elem> + </preprocessorList> + </cTool> + </folder> <folder path="0/HAL-afb"> <cTool> <incDir> @@ -351,8 +413,6 @@ <pElem>../../../opt/include/afb</pElem> </incDir> <preprocessorList> - <Elem>CONTROL_CDEV_RX="/dev/inic-usb-crx"</Elem> - <Elem>CONTROL_CDEV_TX="/dev/inic-usb-ctx"</Elem> <Elem>MAX_SND_CARD=16</Elem> </preprocessorList> </cTool> @@ -365,12 +425,14 @@ <pElem>/usr/include/p11-kit-1</pElem> <pElem>/usr/include/json-c</pElem> <pElem>HAL-afb/HAL-interface</pElem> - <pElem>Shared-Interface</pElem> + <pElem>Audio-Common</pElem> <pElem>build/HAL-afb/HAL-interface</pElem> </incDir> <preprocessorList> + <Elem>CONTROL_FILE_POLICY="onload-control-policy.json"</Elem> + <Elem>CONTROL_PATH_LUA="/etc/default/audio-agent/policy:/home/fulup/.config/audio-agent:/home/fulup/opt/audio-agent/policy:/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/data"</Elem> + <Elem>CONTROL_PATH_POLICY="/etc/default/audio-agent/policy:/home/fulup/.config/audio-agent:/home/fulup/opt/audio-agent/policy:/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/data"</Elem> <Elem>MAX_LINEAR_DB_SCALE=24</Elem> - <Elem>NATIVE_LINUX</Elem> <Elem>TLV_BYTE_SIZE=256</Elem> </preprocessorList> </cTool> @@ -381,6 +443,10 @@ <pElem>HAL-afb/HAL-plugin</pElem> <pElem>../../../opt/include/alsa</pElem> </incDir> + <preprocessorList> + <Elem>CONTROL_CDEV_RX="/dev/inic-usb-crx"</Elem> + <Elem>CONTROL_CDEV_TX="/dev/inic-usb-ctx"</Elem> + </preprocessorList> </cTool> </folder> <folder path="0/HAL-afb/HDA-intel"> @@ -391,12 +457,14 @@ <pElem>/usr/include/p11-kit-1</pElem> <pElem>/usr/include/json-c</pElem> <pElem>HAL-afb/HAL-interface</pElem> - <pElem>Shared-Interface</pElem> + <pElem>Audio-Common</pElem> <pElem>build/HAL-afb/HDA-intel</pElem> </incDir> <preprocessorList> + <Elem>CONTROL_FILE_POLICY="onload-control-policy.json"</Elem> + <Elem>CONTROL_PATH_LUA="/etc/default/audio-agent/policy:/home/fulup/.config/audio-agent:/home/fulup/opt/audio-agent/policy:/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/data"</Elem> + <Elem>CONTROL_PATH_POLICY="/etc/default/audio-agent/policy:/home/fulup/.config/audio-agent:/home/fulup/opt/audio-agent/policy:/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/data"</Elem> <Elem>MAX_LINEAR_DB_SCALE=24</Elem> - <Elem>NATIVE_LINUX</Elem> <Elem>TLV_BYTE_SIZE=256</Elem> <Elem>hal_intel_hda_EXPORTS</Elem> </preprocessorList> @@ -410,12 +478,14 @@ <pElem>/usr/include/p11-kit-1</pElem> <pElem>/usr/include/json-c</pElem> <pElem>HAL-afb/HAL-interface</pElem> - <pElem>Shared-Interface</pElem> + <pElem>Audio-Common</pElem> <pElem>build/HAL-afb/Jabra-Solemate</pElem> </incDir> <preprocessorList> + <Elem>CONTROL_FILE_POLICY="onload-control-policy.json"</Elem> + <Elem>CONTROL_PATH_LUA="/etc/default/audio-agent/policy:/home/fulup/.config/audio-agent:/home/fulup/opt/audio-agent/policy:/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/data"</Elem> + <Elem>CONTROL_PATH_POLICY="/etc/default/audio-agent/policy:/home/fulup/.config/audio-agent:/home/fulup/opt/audio-agent/policy:/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/data"</Elem> <Elem>MAX_LINEAR_DB_SCALE=24</Elem> - <Elem>NATIVE_LINUX</Elem> <Elem>TLV_BYTE_SIZE=256</Elem> <Elem>hal_jabra_usb_EXPORTS</Elem> </preprocessorList> @@ -429,12 +499,14 @@ <pElem>/usr/include/p11-kit-1</pElem> <pElem>/usr/include/json-c</pElem> <pElem>HAL-afb/HAL-interface</pElem> - <pElem>Shared-Interface</pElem> + <pElem>Audio-Common</pElem> <pElem>build/HAL-afb/Scarlett-Focusrite</pElem> </incDir> <preprocessorList> + <Elem>CONTROL_FILE_POLICY="onload-control-policy.json"</Elem> + <Elem>CONTROL_PATH_LUA="/etc/default/audio-agent/policy:/home/fulup/.config/audio-agent:/home/fulup/opt/audio-agent/policy:/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/data"</Elem> + <Elem>CONTROL_PATH_POLICY="/etc/default/audio-agent/policy:/home/fulup/.config/audio-agent:/home/fulup/opt/audio-agent/policy:/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/data"</Elem> <Elem>MAX_LINEAR_DB_SCALE=24</Elem> - <Elem>NATIVE_LINUX</Elem> <Elem>TLV_BYTE_SIZE=256</Elem> <Elem>hal_scalett_usb_EXPORTS</Elem> </preprocessorList> @@ -446,6 +518,10 @@ <pElem>HAL-afb/Unicens-USB</pElem> <pElem>/usr/include/json-c</pElem> </incDir> + <preprocessorList> + <Elem>CONTROL_CDEV_RX="/dev/inic-usb-crx"</Elem> + <Elem>CONTROL_CDEV_TX="/dev/inic-usb-ctx"</Elem> + </preprocessorList> </cTool> </folder> <folder path="0/HighLevel-afb"> @@ -464,47 +540,6 @@ </incDir> </cTool> </folder> - <folder path="0/PolicyCtl-afb"> - <cTool> - <incDir> - <pElem>../../../opt/include</pElem> - <pElem>../../../opt/include/alsa</pElem> - <pElem>/usr/include/p11-kit-1</pElem> - <pElem>/usr/include/json-c</pElem> - <pElem>Shared-Interface</pElem> - <pElem>build/PolicyCtl-afb</pElem> - </incDir> - <preprocessorList> - <Elem>CONTROL_CDEV_RX="/dev/inic-usb-crx"</Elem> - <Elem>CONTROL_CDEV_TX="/dev/inic-usb-ctx"</Elem> - <Elem>MAX_LINEAR_DB_SCALE=24</Elem> - <Elem>MAX_SND_CARD=16</Elem> - <Elem>NATIVE_LINUX</Elem> - <Elem>TLV_BYTE_SIZE=256</Elem> - <Elem>polctl_afb_EXPORTS</Elem> - </preprocessorList> - </cTool> - </folder> - <folder path="0/Shared-Interface"> - <cTool> - <incDir> - <pElem>../../../opt/include</pElem> - <pElem>../../../opt/include/alsa</pElem> - <pElem>/usr/include/p11-kit-1</pElem> - <pElem>/usr/include/json-c</pElem> - <pElem>Shared-Interface</pElem> - <pElem>build/Shared-Interface</pElem> - </incDir> - <preprocessorList> - <Elem>CONTROL_CDEV_RX="/dev/inic-usb-crx"</Elem> - <Elem>CONTROL_CDEV_TX="/dev/inic-usb-ctx"</Elem> - <Elem>MAX_LINEAR_DB_SCALE=24</Elem> - <Elem>MAX_SND_CARD=16</Elem> - <Elem>NATIVE_LINUX</Elem> - <Elem>TLV_BYTE_SIZE=256</Elem> - </preprocessorList> - </cTool> - </folder> </conf> <conf name="Laptop" type="0"> <toolsSet> @@ -595,6 +630,10 @@ <cTool flags="3"> </cTool> </item> + <item path="Audio-Common/audio-common.c" ex="false" tool="0" flavor2="3"> + <cTool flags="3"> + </cTool> + </item> <item path="Common/AudioCommonLib.c" ex="false" tool="0" flavor2="2"> <cTool flags="0"> </cTool> @@ -722,10 +761,6 @@ </item> <item path="MostVolume/libmostvolume.cpp" ex="false" tool="1" flavor2="4"> </item> - <item path="Shared-Interface/audio-interface.c" ex="false" tool="0" flavor2="3"> - <cTool flags="3"> - </cTool> - </item> <folder path="0/ALSA-afb"> <cTool> <incDir> @@ -746,6 +781,16 @@ </incDir> </cTool> </folder> + <folder path="0/Audio-Common"> + <cTool> + <incDir> + <pElem>../../../opt/include/afb</pElem> + <pElem>Shared-Interface</pElem> + <pElem>/usr/include/json-c</pElem> + <pElem>build/Shared-Interface</pElem> + </incDir> + </cTool> + </folder> <folder path="0/Common"> <cTool> <incDir> @@ -856,16 +901,6 @@ </incDir> </cTool> </folder> - <folder path="0/Shared-Interface"> - <cTool> - <incDir> - <pElem>../../../opt/include/afb</pElem> - <pElem>Shared-Interface</pElem> - <pElem>/usr/include/json-c</pElem> - <pElem>build/Shared-Interface</pElem> - </incDir> - </cTool> - </folder> </conf> <conf name="Aplay_Multimedia" type="0"> <toolsSet> @@ -966,8 +1001,16 @@ tool="0" flavor2="2"> </item> + <item path="Audio-Common/audio-common.c" ex="false" tool="0" flavor2="3"> + <cTool flags="1"> + </cTool> + </item> <item path="Common/AudioCommonLib.c" ex="false" tool="0" flavor2="2"> </item> + <item path="Controler-afb/ctl-binding.c" ex="false" tool="0" flavor2="3"> + <cTool flags="0"> + </cTool> + </item> <item path="HAL-afb/HAL-interface/hal-interface.c" ex="false" tool="0" @@ -1074,14 +1117,6 @@ </item> <item path="MostVolume/libmostvolume.cpp" ex="false" tool="1" flavor2="4"> </item> - <item path="PolicyCtl-afb/polctl-binding.c" ex="false" tool="0" flavor2="3"> - <cTool flags="0"> - </cTool> - </item> - <item path="Shared-Interface/audio-interface.c" ex="false" tool="0" flavor2="3"> - <cTool flags="1"> - </cTool> - </item> <item path="build/CMakeFiles/3.5.2/CompilerIdC/CMakeCCompilerId.c" ex="false" tool="0" @@ -1125,6 +1160,37 @@ </preprocessorList> </cTool> </folder> + <folder path="0/Audio-Common"> + <cTool> + <incDir> + <pElem>../../../opt/include/afb</pElem> + <pElem>Shared-Interface</pElem> + <pElem>/usr/include/json-c</pElem> + <pElem>build/Shared-Interface</pElem> + </incDir> + </cTool> + </folder> + <folder path="0/Controler-afb"> + <cTool> + <incDir> + <pElem>../../../opt/include</pElem> + <pElem>../../../opt/include/alsa</pElem> + <pElem>/usr/include/p11-kit-1</pElem> + <pElem>/usr/include/json-c</pElem> + <pElem>Shared-Interface</pElem> + <pElem>build/PolicyCtl-afb</pElem> + </incDir> + <preprocessorList> + <Elem>CONTROL_CDEV_RX="/dev/inic-usb-crx"</Elem> + <Elem>CONTROL_CDEV_TX="/dev/inic-usb-ctx"</Elem> + <Elem>MAX_LINEAR_DB_SCALE=24</Elem> + <Elem>MAX_SND_CARD=16</Elem> + <Elem>NATIVE_LINUX</Elem> + <Elem>TLV_BYTE_SIZE=256</Elem> + <Elem>polctl_afb_EXPORTS</Elem> + </preprocessorList> + </cTool> + </folder> <folder path="0/HAL-afb/HAL-interface"> <cTool> <incDir> @@ -1187,37 +1253,6 @@ </incDir> </cTool> </folder> - <folder path="0/PolicyCtl-afb"> - <cTool> - <incDir> - <pElem>../../../opt/include</pElem> - <pElem>../../../opt/include/alsa</pElem> - <pElem>/usr/include/p11-kit-1</pElem> - <pElem>/usr/include/json-c</pElem> - <pElem>Shared-Interface</pElem> - <pElem>build/PolicyCtl-afb</pElem> - </incDir> - <preprocessorList> - <Elem>CONTROL_CDEV_RX="/dev/inic-usb-crx"</Elem> - <Elem>CONTROL_CDEV_TX="/dev/inic-usb-ctx"</Elem> - <Elem>MAX_LINEAR_DB_SCALE=24</Elem> - <Elem>MAX_SND_CARD=16</Elem> - <Elem>NATIVE_LINUX</Elem> - <Elem>TLV_BYTE_SIZE=256</Elem> - <Elem>polctl_afb_EXPORTS</Elem> - </preprocessorList> - </cTool> - </folder> - <folder path="0/Shared-Interface"> - <cTool> - <incDir> - <pElem>../../../opt/include/afb</pElem> - <pElem>Shared-Interface</pElem> - <pElem>/usr/include/json-c</pElem> - <pElem>build/Shared-Interface</pElem> - </incDir> - </cTool> - </folder> </conf> </confs> </configurationDescriptor> |